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.

Терминальные HTTP-клиенты получают первый кадр страницы в виде ANSI-текста с цветами truecolor вместо HTML-документа. Ничего настраивать не нужно.

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

Запросы, получающие кадр

Запрос получает кадр, если выполнены все условия:

  • метод — GET или HEAD;
  • User-Agent начинается с curl/, Wget/, wget2/, HTTPie/ или xh/;
  • заголовок Accept отсутствует или не содержит text/html.

Браузеры, поисковые роботы и сервисы предпросмотра ссылок получают HTML-страницу. Чтобы получить HTML через curl, запросите его:

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

Кадр выбирается в момент, когда должна быть отдана страница: после middleware, rewrites и обработчиков маршрутов, но никогда для файлов или путей, последний сегмент которых содержит точку. Перенаправления middleware, ответы 401, заголовки и перезаписи применяются к curl так же, как к браузерам. /api/health по-прежнему запускает свой обработчик, а /robots.txt по-прежнему отдаёт файл.

Содержимое кадра

Кадр — это результат первого рендеринга страницы, как в оболочке с пререндерингом: каждая граница <Suspense>, которая ещё ожидает завершения, показывает свой fallback. Асинхронные серверные компоненты не запускаются, а fetch() никогда не завершается, поэтому данные, загружаемые в эффектах, остаются в состоянии загрузки.

  • Размер: 80 × 24 ячейки. Приложение с фиксированной областью просмотра (viewport.responsive: false) использует вместо этого viewport.width × viewport.height.
  • Цвета: 24-битные управляющие последовательности SGR в теме viewport.theme приложения. Каждая строка заканчивается сбросом оформления, а кадр заканчивается переводом строки.
  • Статус: 200 или 404 со страницей приложения «Не найдено», если ни одна страница не подходит или страница вызывает notFound(). Страница, вызывающая redirect() во время рендеринга, отдаёт HTML.

Ответы имеют тип text/plain; charset=utf-8 с заголовками Vary: User-Agent, Accept и Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate, поэтому общие кеши никогда не сохраняют кадр и не отдают его браузеру. HTML-страница по пути, где curl получает кадр, содержит тот же заголовок Vary.

Поддерживаемые среды

ХостСтатические страницыДинамические страницы, неизвестные пути
text devРендеринг по запросуРендеринг по запросу
text start, bunПререндеринг через text buildРендеринг по запросу
vercel, cloudflareПререндеринг через text buildHTML-страница
staticHTML-страницаHTML-страница

Для страниц, отрендеренных на сервере, нужен text start или bun (см. Развёртывание).

Статические хостинги не могут отвечать разным клиентам по-разному, поэтому адаптер static не поддерживает кадры. На Vercel и Cloudflare кадры статических страниц поставляются со сборкой. Динамические страницы ([id], force-dynamic) там отдают HTML, поскольку адаптеры vercel и cloudflare не развёртывают рендерер.

text start и bun dist/server.js рендерят остальные страницы по запросу через .text/server/live.mjs и хранят последние 128 кадров в течение 10 секунд. Каждый сервер рендерит не более 20 некешированных страниц за 10 секунд. После этого curl получает HTML-страницу, пока лимит не восстановится. Если live.mjs не удаётся загрузить на сервере, например потому, что модуль страницы обращается к window на верхнем уровне, эти страницы отдают HTML.

text build сообщает о кадрах, прошедших пререндеринг:

 ✓ Prerendering curl frames (5/5)

Настройка

Измените размер для всего приложения или отключите кадры:

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

Страница может изменить собственный размер или отдавать HTML и терминалам:

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

Размеры ограничены диапазоном 1–500 ячеек. При ansi: false в text.config.ts экспорты страниц игнорируются.

Обычный HTTP

curl не следует перенаправлениям, если не передать -L. Vercel перенаправляет запросы с http:// на https://, как и Cloudflare при включённом параметре Всегда использовать HTTPS, поэтому curl http://… выводит перенаправление вместо кадра. Делитесь URL с https://.

Собственные серверы

@textjs/app/ansi экспортирует вспомогательные функции распознавания запросов и формирования ответов для серверов и функций, которые не запускают text start или адаптер:

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) также принимает (method, headers) с веб-интерфейсом Headers или IncomingHttpHeaders из Node. frameToAnsi(buffer, { theme }) кодирует отрендеренный CellBuffer так же, как это делает фреймворк.

Изменить эту страницу на GitHub