curl and Terminal Clients

curl, Wget, HTTPie and xh get a page's first frame as truecolor ANSI text. Configure its size or turn it off.

Les clients HTTP en terminal reçoivent la première frame d’une page sous forme de texte ANSI en couleurs vraies au lieu du document HTML. Vous n’avez rien à configurer.

curl https://my-app.example.com/about

Requêtes recevant une frame

Une requête reçoit une frame lorsque toutes ces conditions sont réunies :

  • la méthode est GET ou HEAD ;
  • le User-Agent commence par curl/, Wget/, wget2/, HTTPie/ ou xh/ ;
  • l’en-tête Accept est absent ou ne mentionne pas text/html.

Les navigateurs, les robots d’indexation et les aperçus de liens reçoivent la page HTML. Pour obtenir le HTML avec curl, demandez-le :

curl -H 'Accept: text/html' https://my-app.example.com/about

La frame est choisie au moment où la page serait servie : après les middlewares, les rewrites et les gestionnaires de route, et jamais pour les fichiers ou les chemins dont le dernier segment contient un point. Les redirections des middlewares, les réponses 401, les en-têtes et les réécritures s’appliquent à curl comme aux navigateurs. /api/health exécute toujours son gestionnaire et /robots.txt sert toujours le fichier.

Contenu de la frame

La frame est le premier rendu de la page, comme dans une coquille prérendue : chaque limite <Suspense> encore en attente affiche son contenu de repli. Les composants serveur asynchrones ne s’exécutent pas et fetch() ne se résout jamais, donc les données chargées dans les effets restent dans leur état de chargement.

  • Taille : 80 × 24 cellules. Une application avec une zone d’affichage fixe (viewport.responsive: false) utilise plutôt viewport.width × viewport.height.
  • Couleurs : codes d’échappement SGR sur 24 bits dans le viewport.theme de l’application. Chaque ligne se termine par une réinitialisation, et la frame se termine par un saut de ligne.
  • Statut : 200, ou 404 avec la page introuvable de l’application lorsqu’aucune page ne correspond ou que la page appelle notFound(). Une page qui appelle redirect() pendant le rendu sert du HTML.

Les réponses sont de type text/plain; charset=utf-8 avec les en-têtes Vary: User-Agent, Accept et Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate, afin que les caches partagés ne stockent jamais une frame pour la servir à un navigateur. La page HTML à un chemin où curl reçoit une frame porte le même en-tête Vary.

Environnements compatibles

HébergementPages statiquesPages dynamiques, chemins inconnus
text devRendu à la demandeRendu à la demande
text start, bunPrérendu par text buildRendu à la demande
vercel, cloudflarePrérendu par text buildPage HTML
staticPage HTMLPage HTML

Les pages avec rendu côté serveur nécessitent text start ou bun (voir Déploiement).

Les hébergements statiques ne peuvent pas répondre différemment selon le client, donc l’adaptateur static ne prend pas en charge les frames. Sur Vercel et Cloudflare, les frames des pages statiques sont livrées avec le build. Les pages dynamiques ([id], force-dynamic) y servent du HTML, car les adaptateurs vercel et cloudflare ne déploient pas de moteur de rendu.

text start et bun dist/server.js rendent les pages restantes à la demande avec .text/server/live.mjs et conservent les 128 dernières frames pendant 10 secondes. Chaque serveur rend au maximum 20 pages non mises en cache par tranche de 10 secondes. Au-delà, curl reçoit la page HTML jusqu’à ce que le quota se renouvelle. Si live.mjs ne peut pas être chargé sur le serveur, par exemple parce qu’un module de page lit window au niveau supérieur, ces pages servent du HTML.

text build indique les frames prérendues :

 ✓ Prerendering curl frames (5/5)

Configuration

Modifiez la taille pour toute l’application ou désactivez les frames :

text.config.tsts
import { defineConfig } from "@textjs/app";export default defineConfig({  ansi: { cols: 100, rows: 30 },  // ansi: false,});

Une page peut modifier sa propre taille ou servir du HTML aux terminaux également :

app/dashboard/page.tsxtsx
export const ansi = { cols: 120, rows: 40 };// export const ansi = false;

Les tailles sont limitées à 1–500 cellules. Avec ansi: false dans text.config.ts, les exports des pages sont ignorés.

HTTP en clair

curl ne suit pas les redirections à moins que vous ne passiez -L. Vercel redirige les requêtes http:// vers https://, tout comme Cloudflare lorsque l’option Always Use HTTPS est activée, donc curl http://… affiche la redirection au lieu de la frame. Partagez des URL https://.

Serveurs personnalisés

@textjs/app/ansi exporte les fonctions utilitaires de détection et de réponse pour les serveurs et les fonctions qui n’exécutent ni text start ni un adaptateur :

import { ansiResponse, wantsAnsi } from "@textjs/app/ansi";import { frameToAnsi } from "@textjs/react";if (wantsAnsi(request)) return ansiResponse(frameToAnsi(buffer, { theme: "geistDark" }), request.method);

wantsAnsi(request) accepte aussi (method, headers) avec les Headers de l’API Web ou les IncomingHttpHeaders de Node. frameToAnsi(buffer, { theme }) encode un CellBuffer rendu de la même manière que le framework.

Modifier cette page sur GitHub