interface ParsedMediaType { type: string; subtype: string; quality: number; } declare function parseAcceptHeader(header: string): ParsedMediaType[]; declare function mediaTypeMatches(pref: ParsedMediaType, type: string, subtype: string): boolean; declare function negotiateFormat(accept: string, available?: ReadonlyArray): T | null; /** * Decide whether a request should be served the markdown twin instead of HTML. * * This encodes the AEO spec §5 rule for UA-based markdown: a known AI bot MAY * receive markdown, but "UA detection MUST NOT override an explicit `Accept`". * Markdown is served when either: * * 1. RFC 7231 negotiation on the `Accept` header already picks markdown, or * 2. the request is from a known AI bot AND markdown is at least as * acceptable as HTML for that `Accept` header. * * Case 2 is checked by negotiating with markdown listed first, so a tie (a * wildcard Accept, or no Accept at all) resolves to markdown for bots, while an * explicit `Accept: text/html` (where HTML strictly outranks markdown, or * markdown is not acceptable at all) keeps the bot on HTML. */ declare function shouldServeMarkdown(accept: string, isBot: boolean): boolean; type TokenEstimator = (text: string) => number; interface EstimateTokensOptions { tokenizer?: TokenEstimator; } declare function estimateTokens(text: string, options?: EstimateTokensOptions): number; declare function setTokenEstimator(fn: TokenEstimator): void; declare function resetTokenEstimator(): void; declare function normalizeUnicode(text: string): string; interface CleanBodyOptions { stripImages?: boolean; htmlTagReplacements?: Record; collapseBlankLines?: boolean; } declare function cleanBody(body: string, opts?: CleanBodyOptions): string; declare function slugToTitle(slug: string): string; declare function fmtDate(d: Date): string; declare function joinLines(...parts: Array): string; interface MarkdownResponseOptions { cacheControl?: string; noindex?: boolean; redirectFrom?: string; redirectTo?: string; extraHeaders?: HeadersInit; status?: number; tokenizer?: TokenEstimator; } declare function markdownResponse(body: string, options?: MarkdownResponseOptions): Response; declare function injectMarkdownAlternateLink(response: Response, htmlUrl: string, mdUrl: string): Response; /** * URL path utilities for the AEO markdown twin convention. * * Every HTML page at `/foo` has a markdown twin at `/foo.md`. * Root `/` maps to `/index.md`. * * Both helpers are idempotent: calling them on an already-`.md` * path returns it unchanged. */ /** * Convert a URL pathname to its markdown-twin pathname. * * "/" → "/index.md" * "" → "/index.md" * "/blog/" → "/blog.md" * "/blog/post" → "/blog/post.md" * "/blog/x.md" → "/blog/x.md" (idempotent) */ declare function toMarkdownPath(pathname: string): string; /** * Convert a full URL (string or URL) to its markdown-twin URL. * Preserves origin, search, and hash. * * "https://x.com/blog?q=1" → "https://x.com/blog.md?q=1" * "https://x.com/" → "https://x.com/index.md" * "https://x.com/blog.md" → "https://x.com/blog.md" (idempotent) */ declare function toMarkdownUrl(input: string | URL): string; type BotPurpose = "training" | "search" | "user-action" | "unknown"; interface AIBotEntry { name: string; uaPattern: string | RegExp; vendor: string; purpose: BotPurpose; docsUrl?: string; } interface AIBotInfo { isBot: boolean; name: string | null; vendor: string | null; purpose: BotPurpose | null; } declare const AI_BOTS: ReadonlyArray; declare function detectAIBot(userAgent: string): AIBotInfo; interface ListingItem { title: string; href: string; description?: string; } interface ListingOptions { title: string; description: string; url: string; items: ListingItem[]; groupBy?: (item: ListingItem) => string; footer?: string; } declare function listingToMarkdown(opts: ListingOptions): string; interface RelatedLinksGroup { title: string; href: string; } interface RelatedLinks { siblings?: RelatedLinksGroup[]; parent?: RelatedLinksGroup; comparisons?: RelatedLinksGroup[]; glossary?: RelatedLinksGroup[]; blogs?: RelatedLinksGroup[]; docs?: RelatedLinksGroup[]; custom?: RelatedLinksGroup[]; } declare function renderRelatedLinks(related: RelatedLinks): string; interface FAQItem { question: string; answer: string; } declare function renderFAQSection(faqs: FAQItem[]): string; interface PlatformFooter { brand: string; tagline?: string; links: Array<{ title: string; href: string; }>; } interface LlmsTxtSection { title: string; description?: string; links: Array<{ title: string; href: string; description?: string; }>; } interface LlmsTxtOptions { brandName: string; description?: string; sections: LlmsTxtSection[]; } declare function renderLlmsTxt(opts: LlmsTxtOptions): string; type TrailingSlashMode = "never" | "always" | "preserve"; interface AIRequestInfo { url: URL; botName: string | null; botVendor: string | null; acceptHeader: string; pathname: string; cacheStatus: "hit" | "miss"; tokens: number; } interface MissInfo { url: URL; botName: string | null; pathname: string; acceptHeader: string; } declare const AEO_SPEC_VERSION = "1.0"; export { AEO_SPEC_VERSION, type AIBotEntry, type AIBotInfo, type AIRequestInfo, AI_BOTS, type BotPurpose, type CleanBodyOptions, type EstimateTokensOptions, type FAQItem, type ListingItem, type ListingOptions, type LlmsTxtOptions, type LlmsTxtSection, type MarkdownResponseOptions, type MissInfo, type ParsedMediaType, type PlatformFooter, type RelatedLinks, type RelatedLinksGroup, type TokenEstimator, type TrailingSlashMode, cleanBody, detectAIBot, estimateTokens, fmtDate, injectMarkdownAlternateLink, joinLines, listingToMarkdown, markdownResponse, mediaTypeMatches, negotiateFormat, normalizeUnicode, parseAcceptHeader, renderFAQSection, renderLlmsTxt, renderRelatedLinks, resetTokenEstimator, setTokenEstimator, shouldServeMarkdown, slugToTitle, toMarkdownPath, toMarkdownUrl };