Neben dem Client die Anfrage, die der Leser gebaut hat — ausgeschrieben in der
Sprache, in der er arbeitet, und bei jedem Tastendruck neu geschrieben.

## Was mitkommt

| Gruppe       | Clients                                |
| :----------- | :------------------------------------- |
| `curl`       | curl                                   |
| TypeScript   | `fetch`, `$fetch`, `useFetch`, axios   |
| Python       | requests, httpx, urllib                |
| Go           | `net/http`                             |
| PHP          | Guzzle, Laravel `Http`, curl-Extension |

**Sieben sind standardmäßig an:** curl, `fetch`, `$fetch`, Python requests, Go,
Guzzle und Laravel `Http`. Die anderen fünf sind der zweite Client für eine
Sprache, die schon einen auf dem Schirm hat, und eine Auswahl dient dem Wählen
und nicht dem Auflisten alles Vorhandenen.

## Zwei Ebenen

Die Sprache ist der Tab und der Client eine Auswahl darin — PHP mit drei Clients
kostet also einen Tab. Eine Site, die drei eigene für eine Sprache ergänzt,
kostet weiterhin einen. Die Wahl des Lesers wird über Seiten hinweg behalten: Wer
in curl arbeitet, arbeitet beim nächsten Endpoint auch in curl.

## Die Liste konfigurieren

```ts
export default defineAppConfig({
  duxt: {
    requestSamples: ['curl', 'php-guzzle', 'php-laravel']
  }
})
```

Ein String wählt einen der mitgelieferten; die ids sind `curl`, `fetch`,
`ofetch`, `use-fetch`, `axios`, `python-requests`, `python-httpx`,
`python-urllib`, `go`, `php-guzzle`, `php-laravel`, `php-curl`.

Die Liste **ersetzt** statt zu ergänzen, wie jede andere Liste in der Config —
sonst könnte eine Site, die eine PHP-API dokumentiert, ihre Clients hinzufügen,
aber die ungewollten nie loswerden. Die Reihenfolge ist die Reihenfolge, in der
sie erscheinen.

## Ein eigener Generator

Ein Objekt ergänzt einen eigenen — oder ersetzt einen mitgelieferten, indem es
seine id wiederverwendet.

```ts
requestSamples: [
  'curl',
  {
    id: 'ruby',
    group: 'Ruby',
    label: 'Net::HTTP',
    language: 'ruby',
    generate: (request) => `
uri = URI('${request.url}')
Net::HTTP.get(uri)
`.trim()
  }
]
```

| Feld       | Typ                   | Anmerkungen                                        |
| :--------- | :-------------------- | :------------------------------------------------- |
| `id`       | `string`              | Stabil: Es ist auch der gemerkte Wert              |
| `group`    | `DuxtText`            | Der Tab                                            |
| `label`    | `DuxtText`            | Der Eintrag innerhalb des Tabs                     |
| `language` | `string`              | Eine Shiki-id — sie entscheidet über die Grammatik |
| `generate` | `(request) => string` | Synchron, pur, kein Netz, kein DOM                 |

`generate` läuft im **Browser**, bei jedem Tastendruck, weil das Sample der
Anfrage folgen muss, die bearbeitet wird. `request` ist
`{ method, url, headers, body?, responseType? }`, wobei `body` der Text des Editors ist und
nicht geparst — ein Generator, der ihn als Daten will, parst ihn selbst und
entscheidet selbst, was gilt, wenn es kein JSON ist.


### Warum TypeScript und nicht JavaScript

`$fetch`, `useFetch` und axios nehmen die Form der Antwort als Typparameter, und
`responseType` füllt ihn: `$fetch<Widget>(…)` ist die Zeile, die jemand
tatsächlich schreibt. `fetch` hat keinen — es liefert ein `Response`, was auch
immer im Body steht — sein Sample ist also in beiden Sprachen dasselbe.

Genau deshalb gibt es eine Gruppe und nicht zwei. Eine JavaScript-Gruppe daneben
trüge ein `fetch`, das sich nie unterscheidet, und drei Clients, die sich nur da
unterscheiden, wo das Dokument sein Antwort-Schema benannt hat — für doppelt so
viele Tabs.

`responseType` ist der Komponentenname dessen, was eine erfolgreiche Antwort
zurückgibt — `Widget`, oder `Widget[]` für eine Liste. Er fehlt, wo das
Antwort-Schema inline steht statt hinter einem `$ref`, und ein Sample hat dann
einfach keinen Typparameter: hier wird kein Name erfunden, den das Dokument
nicht gegeben hat.

## Was ein Sample zeigt

**Die Anfrage, und nichts danach.** Die Antwort steht schon auf dem Schirm, im
Bereich unter dem Editor — ein Sample, das sie ein zweites Mal holt, würde dem
Leser etwas zeigen, das er sehen kann, zum Preis eines Fehlerzweigs in jeder
Sprache, die einen hat.

Go ist die Ausnahme und behält seine `err`-Prüfungen: Ohne sie ist es kein Go,
und ein Schnipsel, der nicht kompiliert, ist schlimmer als ein langer.

## Grammatiken werden bezahlt, nicht angenommen

Diese Samples werden im Browser gefärbt, jede Sprache ist also Bytes, die ein
Leser lädt — anders als bei einem Markdown-Fence, dessen Grammatik eine
Build-Zeit-Kosten ist und nie einen Browser erreicht.

Deshalb wird das Grammatik-Set **aus der Liste oben generiert**: Eine Site
liefert genau die Sprachen aus, die ihre Samples nennen. Die Default-Sieben
brauchen sechs Grammatiken. Shikis eigene Lazy-Map zu laden würde 3 KB kosten und
jeden Build 242 Grammatik-Chunks emittieren lassen.

## Samples aus dem Dokument

`x-codeSamples` — in beiden Schreibweisen, die ältere `x-code-samples`
eingeschlossen — wird an einer Operation gelesen und **ersetzt die generierten
Samples für seine Sprache**. Wer die API besitzt, kennt ihre Idiome besser als
ein Generator.

```yaml
paths:
  /pets:
    get:
      x-codeSamples:
        - lang: ruby
          label: Net::HTTP
          source: |
            Net::HTTP.get(URI('https://api.example.com/pets'))
```

Ein solches Sample ist **statisch** — es kann den Änderungen des Lesers nicht
folgen — deshalb sagt der Bereich, wer es geschrieben hat und dass es den
Änderungen darüber nicht folgt. Ein Leser, der den Body geändert hat und dann
dorthin wechselt, würde sonst schließen, der Editor sei kaputt, statt dass dieses
Sample fest ist.

Seine Sprache ist dort nicht sichtbar, wo das Grammatik-Set geschrieben wird: Eine
entfernte Quelle ist noch nicht geklont. Also nenne sie:

```ts
duxt: {
  sampleLanguages: ['ruby']
}
```

Ohne das rendert das Sample als reiner Text.