import Foundation /// Fetches and parses man.sr.ht wiki pages over plain HTTP (no auth required). struct ManPage: Sendable { let url: URL let title: String let contentHTML: String } struct ManPageService { static let baseURL = URL(string: "https://man.sr.ht")! static let pagesBaseURL = URL(string: "https://srht.site/")! /// Fetches a man.sr.ht page and extracts the article content. /// Uses an unauthenticated URLSession because man.sr.ht pages are public. static func fetch(url: URL) async throws -> ManPage { guard isTrustedDocumentationURL(url) else { throw URLError(.badURL) } var request = URLRequest(url: url) request.setValue(Bundle.main.hutchUserAgent, forHTTPHeaderField: "User-Agent") let (data, response) = try await URLSession.shared.data(for: request) if let http = response as? HTTPURLResponse, !(200...299).contains(http.statusCode) { throw URLError(.badServerResponse) } guard let html = String(data: data, encoding: .utf8) else { throw URLError(.cannotDecodeContentData) } return ManPage( url: url, title: extractTitle(from: html, fallbackURL: url), contentHTML: sanitizeContentHTML(extractContent(from: html)) ) } static func isTrustedDocumentationURL(_ url: URL) -> Bool { guard url.scheme?.localizedCaseInsensitiveCompare("https") == .orderedSame, let host = url.host?.lowercased() else { return false } return host == "man.sr.ht" || host.hasSuffix(".man.sr.ht") || host == "srht.site" } private static func extractTitle(from html: String, fallbackURL: URL) -> String { if let headerHTML = substring( in: html, startingAtFirstOccurrenceOf: #"