// author: simon-pierre boucher export const SAFARI_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15"; export const BOT_SUFFIX = " Tendril/1.0 (+https://www.ten-dril.com/bot)"; export const DEFAULT_USER_AGENT = SAFARI_UA + BOT_SUFFIX; /** * Build request headers in Safari's emission order (§3.2). Header order is a * fingerprint, so this returns an insertion-ordered object; do not sort it. * The bot suffix in the UA is non-negotiable (§26). */ export function buildHeaders( host: string, overrides?: Readonly>, userAgent?: string, ): Record { const headers: Record = { Host: host, Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.9", "Accept-Encoding": "gzip, deflate, br", "User-Agent": (userAgent ?? SAFARI_UA) + BOT_SUFFIX, Connection: "keep-alive", "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Sec-Fetch-User": "?1", "Upgrade-Insecure-Requests": "1", }; if (overrides) { for (const [key, value] of Object.entries(overrides)) headers[key] = value; } return headers; }