/** Pure HTML extraction for site onboarding crawls. No network. */ import { type SocialLink } from "./social.ts"; export type ExtractedImage = { src: string; alt: string; width?: number; height?: number; inChrome?: boolean; fromJsonLdLogo?: boolean; fromOg?: boolean; aboveFold?: boolean; pageUrl?: string; nearbyHeading?: string; }; export type ExtractedCta = { href: string; label: string; kind: "tel" | "mailto" | "internal" | "external" | "other"; }; export type ExtractedTestimonial = { quote: string; attribution?: string; }; export type ExtractedFaq = { question: string; answer: string; }; export type ExtractedPage = { aliasOf?: string; url: string; title: string; description: string; canonical?: string; lang?: string; h1: string[]; headings: string[]; outline: string[]; paragraphs: string[]; lists: string[]; testimonials: ExtractedTestimonial[]; faqs: ExtractedFaq[]; links: { href: string; label: string; relative?: boolean; }[]; ctas: ExtractedCta[]; images: ExtractedImage[]; phones: string[]; emails: string[]; social: SocialLink[]; jsonLd: unknown[]; audit: PageAuditHints; }; export type PageAuditHints = { h1Count: number; missingAlts: number; unlabeledInputs: number; hasCanonical: boolean; hasLang: boolean; hasJsonLd: boolean; titleLength: number; descriptionLength: number; }; export declare function shouldSkipPath(pathname: string): boolean; /** Drop runaway query-string variants while keeping unique content URLs. */ export declare function shouldSkipQueryUrl(href: string): boolean; export declare function stripNoise(html: string): string; export declare function absolutize(href: string, base: string): string | null; /** Resolve `` against the document URL; fall back to the page URL. */ export declare function documentBase(html: string, pageUrl: string): string; /** Path-relative href (about, ../x) as opposed to root-relative, absolute, or hash. */ export declare function isPathRelativeHref(href: string): boolean; export declare function sameOrigin(a: string, b: string): boolean; /** Treat www and apex as the same site for crawl bounds. */ export declare function sameSite(a: string, b: string): boolean; export declare function isLikelyTrackingImage(img: ExtractedImage): boolean; export declare function extractPhones(text: string): string[]; export declare function extractEmails(text: string): string[]; export declare function parseJsonLd(html: string): unknown[]; export declare function businessFromJsonLd(nodes: unknown[]): { name?: string; phone?: string; email?: string; address?: string; city?: string; hours?: string[]; url?: string; logoUrl?: string; }; export declare function jsonLdServices(nodes: unknown[]): { name: string; blurb: string; }[]; /** Builders often put their prose in divs. Keep leaf prose without duplicating * parent containers, headings, controls or existing semantic paragraphs. */ export declare function extractNarrativeParagraphs(html: string): string[]; export declare function extractPage(html: string, pageUrl: string): ExtractedPage; export declare function parseSitemapLocs(xml: string): string[]; export declare function inferPrimaryCta(ctas: ExtractedCta[]): ExtractedCta | undefined; /** Slugify a page URL into a safe copy filename stem. */ export declare function pageCopySlug(pageUrl: string, index: number): string;