aeorank 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parked-domain.ts","../src/site-crawler.ts","../src/scoring.ts","../src/headless-fetch.ts","../src/scorecard-builder.ts","../src/narrative-generator.ts","../src/multi-page-fetcher.ts","../src/page-scorer.ts","../src/page-analyzer.ts","../src/audit.ts","../src/link-graph.ts","../src/fix-engine.ts","../src/html-report.ts","../src/compare.ts"],"sourcesContent":["/**\n * Parked domain detection.\n * Vendored from @aeo/queue/redirect-check (pure functions, no network).\n */\n\nexport interface ParkedDomainResult {\n isParked: boolean;\n reason?: string;\n}\n\n/** Known parking paths used by domain parking services */\nconst PARKING_PATHS = ['/lander', '/parking', '/park', '/sedoparking'];\n\n/** Known parking service domains found in page HTML (scripts, iframes, links) */\nconst PARKING_SERVICE_DOMAINS = [\n 'sedoparking.com',\n 'parkingcrew.net',\n 'bodis.com',\n 'dsparking.com',\n 'hugedomains.com',\n 'afternic.com',\n 'dan.com',\n 'undeveloped.com',\n 'domainmarket.com',\n 'sav.com',\n 'domaincontrol.com',\n 'above.com',\n 'domainlore.com',\n 'domainnamesales.com',\n 'brandbucket.com',\n 'squadhelp.com',\n 'godaddy.com/domainsearch',\n];\n\n/** Parking-specific text patterns (case-insensitive) */\nconst PARKING_TEXT_PATTERNS = [\n /\\bbuy this domain\\b/i,\n /\\bdomain is for sale\\b/i,\n /\\bthis domain may be for sale\\b/i,\n /\\bdomain for sale\\b/i,\n /\\bthis domain name is available\\b/i,\n /\\bparked by/i,\n /\\bthis page is parked/i,\n /\\bdomain has expired/i,\n /\\bthis domain has been registered/i,\n /\\bmake an offer on this domain\\b/i,\n /\\bget this domain\\b/i,\n /\\bacquire this domain\\b/i,\n];\n\nfunction detectParkingRedirect(bodySnippet: string): string | null {\n const relativeRedirect = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"](\\/[^'\"]*)['\"]/i,\n );\n if (!relativeRedirect) return null;\n const path = relativeRedirect[2].toLowerCase().replace(/[?#].*/, '');\n if (PARKING_PATHS.includes(path)) {\n return `js-redirect to ${relativeRedirect[2]}`;\n }\n return null;\n}\n\nfunction detectParkingService(bodySnippet: string): string | null {\n const lower = bodySnippet.toLowerCase();\n for (const service of PARKING_SERVICE_DOMAINS) {\n if (lower.includes(service)) {\n return `parking service: ${service}`;\n }\n }\n return null;\n}\n\nfunction detectParkingText(bodySnippet: string): string | null {\n for (const pattern of PARKING_TEXT_PATTERNS) {\n if (pattern.test(bodySnippet)) {\n return `parking text: ${bodySnippet.match(pattern)?.[0]}`;\n }\n }\n return null;\n}\n\n/**\n * Detect if a page is a parked/lost/for-sale domain.\n * Pure function - no network calls.\n */\nexport function detectParkedDomain(bodySnippet: string): ParkedDomainResult {\n const parkingRedirect = detectParkingRedirect(bodySnippet);\n if (parkingRedirect) return { isParked: true, reason: parkingRedirect };\n\n const parkingService = detectParkingService(bodySnippet);\n if (parkingService) return { isParked: true, reason: parkingService };\n\n const parkingText = detectParkingText(bodySnippet);\n if (parkingText) return { isParked: true, reason: parkingText };\n\n return { isParked: false };\n}\n","import type { AuditFinding, AuditStatus, Priority } from './types.js';\nimport { detectParkedDomain } from './parked-domain.js';\n\nexport interface CriterionResult {\n criterion: string;\n criterion_label: string;\n score: number;\n status: AuditStatus;\n findings: AuditFinding[];\n fix_priority: Priority;\n}\n\n// ─── Pre-fetched site data (one fetch per URL, reused across all criteria) ───\n\nexport type PageCategory = 'homepage' | 'blog' | 'about' | 'pricing' | 'services'\n | 'contact' | 'team' | 'resources' | 'docs' | 'cases' | 'faq' | 'content';\n\nexport interface FetchResult {\n text: string;\n status: number;\n finalUrl?: string;\n category?: PageCategory;\n}\n\nexport interface SiteData {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage: FetchResult | null;\n llmsTxt: FetchResult | null;\n robotsTxt: FetchResult | null;\n faqPage: FetchResult | null;\n sitemapXml: FetchResult | null;\n rssFeed: FetchResult | null;\n aiTxt: FetchResult | null;\n /** Set when homepage redirects to a different (non-brand) domain */\n redirectedTo: string | null;\n /** Set when homepage is a parked/for-sale/lost domain */\n parkedReason: string | null;\n /** Sampled blog/content pages from sitemap (up to 50) */\n blogSample?: FetchResult[];\n /** Full-crawl statistics (set when --full-crawl is used) */\n crawlStats?: {\n discovered: number;\n fetched: number;\n skipped: number;\n elapsed: number;\n };\n /** Link graph from full-site crawl */\n linkGraph?: import('./link-graph.js').LinkGraph;\n}\n\n// Raw data summary for AI narrative generation\nexport interface RawDataSummary {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage_length: number;\n homepage_text_length: number;\n has_https: boolean;\n llms_txt_status: number | null;\n llms_txt_length: number;\n robots_txt_status: number | null;\n robots_txt_snippet: string;\n robots_txt_ai_crawlers: string[];\n robots_txt_blocked_crawlers: string[];\n schema_types_found: string[];\n schema_block_count: number;\n faq_page_status: number | null;\n faq_page_length: number;\n sitemap_status: number | null;\n internal_link_count: number;\n external_link_count: number;\n question_headings_count: number;\n h1_count: number;\n has_meta_description: boolean;\n has_title: boolean;\n has_phone: boolean;\n has_address: boolean;\n has_org_schema: boolean;\n has_social_links: boolean;\n semantic_elements_found: string[];\n img_count: number;\n img_with_alt_count: number;\n has_lang_attr: boolean;\n has_aria: boolean;\n has_breadcrumbs: boolean;\n has_nav: boolean;\n has_footer: boolean;\n has_case_studies: boolean;\n has_statistics: boolean;\n has_expert_attribution: boolean;\n has_blog_section: boolean;\n // New criteria fields\n has_date_modified_schema: boolean;\n time_element_count: number;\n sitemap_url_count: number;\n has_rss_feed: boolean;\n table_count: number;\n ordered_list_count: number;\n unordered_list_count: number;\n definition_pattern_count: number;\n has_ai_txt: boolean;\n has_person_schema: boolean;\n fact_data_point_count: number;\n has_canonical: boolean;\n has_license_schema: boolean;\n sitemap_recent_lastmod_count: number;\n rendered_with_headless?: boolean;\n // Speakable schema fields\n has_speakable_schema: boolean;\n speakable_selector_count: number;\n // Blog sample fields\n blog_sample_count: number;\n blog_sample_urls: string[];\n blog_sample_schema_types: string[];\n blog_sample_question_headings: number;\n blog_sample_faq_schema_found: boolean;\n // Criteria 24-26 fields\n question_heading_answer_rate: number; // -1 = N/A, 0-100 = %\n question_heading_total: number;\n cannibalizing_pairs_count: number;\n page_titles_sampled: number;\n has_visible_date: boolean;\n has_schema_date_in_ld: boolean;\n date_modified_recency_days: number | null;\n // Full-crawl stats\n crawl_discovered: number;\n crawl_fetched: number;\n crawl_skipped: number;\n}\n\nasync function fetchText(url: string): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(15000),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n const text = await res.text();\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n/** Extract bare domain from URL (no protocol, www, port, path) */\nfunction extractDomain(url: string): string {\n return url.replace(/^https?:\\/\\//, '').replace(/\\/.*/, '').replace(/:[0-9]+$/, '').replace(/^www\\./, '').toLowerCase();\n}\n\n/** Extract brand name from domain (everything before TLD) */\nfunction extractBrandName(domain: string): string {\n const parts = domain.split('.');\n const twoPartTlds = ['co.uk', 'com.au', 'co.jp', 'com.br', 'co.nz', 'co.in'];\n const lastTwo = parts.slice(-2).join('.');\n if (twoPartTlds.includes(lastTwo) && parts.length > 2) {\n return parts.slice(0, -2).join('.');\n }\n return parts.length > 1 ? parts.slice(0, -1).join('.') : domain;\n}\n\n/** Check if redirect target is a different brand (hijacked domain) */\nfunction detectCrossDomainRedirect(originalDomain: string, homepage: FetchResult): string | null {\n if (!homepage.finalUrl) return null;\n const finalDomain = extractDomain(homepage.finalUrl);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n finalDomain === cleanOriginal ||\n finalDomain === `www.${cleanOriginal}` ||\n extractBrandName(finalDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return finalDomain;\n}\n\n/** Detect JS-based cross-domain redirect in HTML body */\nfunction detectJsRedirect(bodySnippet: string, originalDomain: string): string | null {\n const jsMatch = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"]https?:\\/\\/([^'\"]+)['\"]/i,\n );\n if (!jsMatch) return null;\n const jsDomain = extractDomain('https://' + jsMatch[2]);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n jsDomain === cleanOriginal ||\n jsDomain === `www.${cleanOriginal}` ||\n extractBrandName(jsDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return jsDomain;\n}\n\n/** Detect HTML served for plain-text URLs (e.g. catch-all routes returning 200 for /ai.txt) */\nfunction isHtmlResponse(result: FetchResult | null): boolean {\n if (!result || result.status !== 200) return false;\n const trimmed = result.text.trimStart().slice(0, 200).toLowerCase();\n return trimmed.startsWith('<!doctype html') || trimmed.startsWith('<html') || /<head[\\s>]/i.test(trimmed);\n}\n\n/**\n * Fetches all site data in parallel with HTTPS/HTTP fallback.\n * Single entry point for all HTTP requests - no redundant fetches.\n */\nexport async function prefetchSiteData(domain: string): Promise<SiteData> {\n // Step 1: Detect protocol (HTTPS first, fallback to HTTP)\n let protocol: 'https' | 'http' | null = null;\n let homepage: FetchResult | null = null;\n\n homepage = await fetchText(`https://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'https';\n } else {\n homepage = await fetchText(`http://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'http';\n }\n }\n\n if (!protocol) {\n return { domain, protocol: null, homepage: null, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: null, blogSample: [] };\n }\n\n // Check for cross-domain redirect (hijacked/expired domains)\n const httpRedirect = homepage ? detectCrossDomainRedirect(domain, homepage) : null;\n const jsRedirect = homepage ? detectJsRedirect(homepage.text.slice(0, 8192), domain) : null;\n const redirectedTo = httpRedirect || jsRedirect;\n\n if (redirectedTo) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo, parkedReason: null, blogSample: [] };\n }\n\n // Check for parked/lost/for-sale domains\n const parkedResult = homepage ? detectParkedDomain(homepage.text.slice(0, 8192)) : { isParked: false };\n if (parkedResult.isParked) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: parkedResult.reason || 'parked', blogSample: [] };\n }\n\n const baseUrl = `${protocol}://${domain}`;\n\n // Step 2: Fetch all other resources in parallel\n const [llmsTxt, robotsTxt, faqPage, sitemapXml, aiTxt] = await Promise.all([\n fetchText(`${baseUrl}/llms.txt`),\n fetchText(`${baseUrl}/robots.txt`),\n fetchText(`${baseUrl}/faq`).then(async (result) => {\n if (result && result.status === 200) return result;\n // Fallback chain for FAQ page variants\n for (const path of ['/frequently-asked-questions', '/help', '/support', '/help-center']) {\n const fallback = await fetchText(`${baseUrl}${path}`);\n if (fallback && fallback.status === 200) return fallback;\n }\n return result;\n }),\n fetchText(`${baseUrl}/sitemap.xml`),\n fetchText(`${baseUrl}/ai.txt`),\n ]);\n\n // Step 3: Discover RSS feed URL from homepage, then fetch it\n let rssFeed: FetchResult | null = null;\n if (homepage) {\n const rssLinkMatch = homepage.text.match(/<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"[^>]*href=\"([^\"]*)\"[^>]*>/i);\n if (rssLinkMatch) {\n const rssUrl = rssLinkMatch[1].startsWith('http') ? rssLinkMatch[1] : `${baseUrl}${rssLinkMatch[1]}`;\n rssFeed = await fetchText(rssUrl);\n }\n if (!rssFeed || rssFeed.status !== 200) {\n // Fallback: try common RSS paths\n for (const path of ['/feed', '/rss.xml', '/feed.xml']) {\n rssFeed = await fetchText(`${baseUrl}${path}`);\n if (rssFeed && rssFeed.status === 200 && (rssFeed.text.includes('<rss') || rssFeed.text.includes('<feed') || rssFeed.text.includes('<channel'))) break;\n rssFeed = null;\n }\n }\n }\n\n // Step 4: Sample blog pages from sitemap (up to 5)\n let blogSample: FetchResult[] = [];\n if (sitemapXml && sitemapXml.status === 200) {\n let sitemapForBlog = sitemapXml.text;\n\n // If sitemapindex, fetch the best sub-sitemap first\n const subSitemapUrl = extractSubSitemapUrl(sitemapForBlog);\n if (subSitemapUrl) {\n const subSitemap = await fetchText(subSitemapUrl);\n if (subSitemap && subSitemap.status === 200) {\n sitemapForBlog = subSitemap.text;\n }\n }\n\n const blogUrls = extractBlogUrlsFromSitemap(sitemapForBlog, domain, 50);\n if (blogUrls.length > 0) {\n const fetched = await Promise.all(blogUrls.map(url => fetchText(url)));\n blogSample = fetched.filter((r): r is FetchResult =>\n r !== null && r.status === 200 && r.text.length > 500\n );\n // Tag blog sample pages\n for (const page of blogSample) {\n page.category = 'blog';\n }\n }\n }\n\n // Tag homepage\n if (homepage) homepage.category = 'homepage';\n\n return { domain, protocol, homepage, llmsTxt, robotsTxt, faqPage, sitemapXml, rssFeed, aiTxt, redirectedTo: null, parkedReason: null, blogSample };\n}\n\n// ─── Blog sample helpers ─────────────────────────────────────────────────────\n\n/** Strip executable script/style tags to avoid inline JS polluting text analysis.\n * Preserves <script type=\"application/ld+json\"> (JSON-LD structured data). */\nfunction stripScripts(html: string): string {\n return html\n .replace(/<script(?![^>]*type\\s*=\\s*[\"']application\\/ld\\+json[\"'])[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '');\n}\n\n/** Concatenate homepage + blog sample HTML for combined analysis */\nfunction getCombinedHtml(data: SiteData): string {\n const parts = [stripScripts(data.homepage?.text || '')];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n parts.push(stripScripts(page.text));\n }\n }\n return parts.join('\\n');\n}\n\n/** Get blog-only HTML concatenated */\nfunction getBlogHtml(data: SiteData): string {\n if (!data.blogSample || data.blogSample.length === 0) return '';\n return data.blogSample.map(p => stripScripts(p.text)).join('\\n');\n}\n\n// ─── Criterion checks (all use pre-fetched SiteData) ────────────────────────\n\n// Criterion 1: llms.txt\nfunction checkLlmsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.llmsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n const statusNote = result ? (isHtmlResponse(result) ? 'HTML page served (not a valid text file)' : `HTTP ${result.status}`) : 'connection failed';\n findings.push({ severity: 'critical', detail: `No llms.txt file found at ${data.protocol}://${data.domain}/llms.txt (${statusNote})`, fix: 'Create a /llms.txt file that describes your site, services, and key pages in markdown format' });\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: 0, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text;\n let score = 4; // exists\n\n if (text.length < 100) {\n findings.push({ severity: 'medium', detail: `llms.txt exists but is very short (${text.length} characters)`, fix: 'Add comprehensive description of your services, team, and key content' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: `llms.txt file found (${text.length} characters)` });\n }\n\n if (text.includes('#') || text.includes('##')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt uses markdown headings for structure' });\n } else {\n findings.push({ severity: 'low', detail: 'llms.txt lacks markdown structure', fix: 'Add headings (# About, ## Services, etc.) for better LLM parsing' });\n }\n\n if (/https?:\\/\\//.test(text)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt includes URLs to key pages' });\n } else {\n findings.push({ severity: 'medium', detail: 'llms.txt does not link to key pages', fix: 'Add URLs to your most important pages (services, about, FAQ)' });\n }\n\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: Math.min(10, score), status: score >= 7 ? 'pass' : 'partial', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// Criterion 2: Schema Markup\nfunction checkSchemaMarkup(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage to check schema markup' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD structured data found on homepage', fix: 'Add Organization, LocalBusiness, or WebSite schema in a <script type=\"application/ld+json\"> tag' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${ldJsonMatches.length} JSON-LD block(s) on homepage` });\n\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundTypes: string[] = [];\n\n for (const type of schemaTypes) {\n if (allSchemaText.includes(`\"${type}\"`) || allSchemaText.includes(`\"@type\":\"${type}\"`)) {\n foundTypes.push(type);\n }\n }\n\n if (foundTypes.length > 0) {\n score += Math.min(4, foundTypes.length * 2);\n findings.push({ severity: 'info', detail: `Schema types found: ${foundTypes.join(', ')}` });\n }\n\n if (!foundTypes.includes('organization') && !foundTypes.includes('localbusiness')) {\n findings.push({ severity: 'high', detail: 'Missing Organization or LocalBusiness schema', fix: 'Add Organization schema with name, url, logo, contactPoint, and sameAs properties' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'Organization or LocalBusiness schema found' });\n }\n\n if (!foundTypes.includes('faqpage')) {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema found', fix: 'Add FAQPage schema on pages with FAQ content' });\n } else {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema markup present' });\n }\n\n // Blog sample enhancement: additional schema types from blog posts\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = getBlogHtml(data);\n const blogLdJson = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (blogLdJson.length > 0) {\n const blogSchemaText = blogLdJson.join(' ').toLowerCase();\n const blogTypes = schemaTypes.filter(t =>\n (blogSchemaText.includes(`\"${t}\"`) || blogSchemaText.includes(`\"@type\":\"${t}\"`)) && !foundTypes.includes(t)\n );\n if (blogTypes.length > 0) {\n score += Math.min(2, blogTypes.length);\n findings.push({ severity: 'info', detail: `Additional schema types found on blog pages: ${blogTypes.join(', ')}` });\n }\n // FAQPage on blog is especially valuable\n if (!foundTypes.includes('faqpage') && /faqpage/i.test(blogSchemaText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema found on blog posts' });\n }\n }\n }\n\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 3: Q&A Content Format\nfunction checkQAFormat(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Count question headings from homepage + blog sample combined\n const combinedHtml = getCombinedHtml(data);\n const hTagContent = (combinedHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n\n if (questionHeadings.length >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${questionHeadings.length} question-format heading(s) found`, fix: 'Structure more content as Q&A with question headings (H2/H3) followed by direct answers' });\n } else {\n findings.push({ severity: 'high', detail: 'No question-format headings found', fix: 'Add Q&A sections with headings like \"What is...?\", \"How does...?\" followed by concise answers' });\n }\n\n // Check for question heading followed by a paragraph (20-500 chars, allowing nested inline tags)\n const hasDirectAnswers = /<h[2-3][^>]*>[^<]*\\?<\\/h[2-3]>\\s*<p[^>]*>[\\s\\S]{20,500}<\\/p>/i.test(combinedHtml);\n if (hasDirectAnswers) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Content uses direct-answer format after question headings' });\n } else {\n findings.push({ severity: 'medium', detail: 'Content does not follow direct-answer format', fix: 'Start each answer paragraph with a concise 1-2 sentence definition before elaborating' });\n }\n\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Proper single H1 tag hierarchy' });\n } else if (h1Count === 0) {\n findings.push({ severity: 'high', detail: 'No H1 tag found', fix: 'Add exactly one H1 tag as the main page heading' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple H1 tags found (${h1Count})`, fix: 'Use only one H1 per page; use H2/H3 for subsections' });\n }\n\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 4: Clean HTML (includes HTTPS check - v4 requirement)\nfunction checkCleanHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n const httpsAvailable = data.protocol === 'https';\n\n // HTTPS check (v4 requirement: no HTTPS = cap at 3)\n if (httpsAvailable) {\n findings.push({ severity: 'info', detail: 'Site serves over HTTPS' });\n } else {\n findings.push({ severity: 'critical', detail: 'Site does not support HTTPS', fix: 'Enable HTTPS with a valid SSL certificate. Sites without HTTPS are penalized by AI crawlers.' });\n }\n\n // Check for semantic elements\n const hasMain = /<main[\\s>]/i.test(html);\n const hasArticle = /<article[\\s>]/i.test(html);\n const hasSection = /<section[\\s>]/i.test(html);\n\n const semanticCount = [hasMain, hasArticle, hasSection].filter(Boolean).length;\n score += Math.min(3, semanticCount * 1);\n if (semanticCount >= 2) {\n findings.push({ severity: 'info', detail: `Uses semantic HTML5 elements: ${[hasMain && 'main', hasArticle && 'article', hasSection && 'section'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'medium', detail: 'Limited semantic HTML5 usage', fix: 'Wrap main content in <main>, use <article> for standalone content, <section> for grouped content' });\n }\n\n // Check H1 count\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Single H1 tag found - correct heading hierarchy' });\n } else {\n findings.push({ severity: h1Count === 0 ? 'high' : 'medium', detail: `${h1Count === 0 ? 'No' : 'Multiple'} H1 tag(s) found (${h1Count})`, fix: 'Use exactly one H1 per page' });\n }\n\n // Check for text content (not JS-only)\n const textContent = html.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n if (textContent.length > 500) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Page has substantial text content accessible without JavaScript' });\n } else {\n findings.push({ severity: 'high', detail: 'Very little text content visible in HTML source', fix: 'Ensure key content is server-rendered, not loaded via JavaScript only' });\n }\n\n // Check for meta tags\n const hasMetaDesc = /<meta[^>]*name=\"description\"[^>]*>/i.test(html);\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n if (hasMetaDesc && hasTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Page has title and meta description' });\n } else {\n findings.push({ severity: 'medium', detail: `Missing ${!hasTitle ? 'title tag' : ''}${!hasTitle && !hasMetaDesc ? ' and ' : ''}${!hasMetaDesc ? 'meta description' : ''}`, fix: 'Add <title> and <meta name=\"description\"> tags' });\n }\n\n // HTTPS cap: no HTTPS = max 3 for this criterion\n if (!httpsAvailable) {\n score = Math.min(score, 3);\n }\n\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 5: Entity Consistency\nfunction checkEntityConsistency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Check for phone numbers with context validation\n // A regex match only counts if it has supporting context (tel: link, schema, or nearby keywords)\n const hasTelLink = /href=\"tel:/i.test(html);\n const hasSchemaTelephone = /\"telephone\"/i.test(html);\n const phoneContextWords = /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i;\n\n const phoneRegex = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n const phones = text.match(phoneRegex) || [];\n const contextValidatedPhones: string[] = [];\n\n if (hasTelLink || hasSchemaTelephone) {\n // Global context validates all matches\n contextValidatedPhones.push(...phones);\n } else {\n // Check each match for nearby context words (~100 chars around match)\n let match: RegExpExecArray | null;\n const phoneRegex2 = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n while ((match = phoneRegex2.exec(text)) !== null) {\n const start = Math.max(0, match.index - 100);\n const end = Math.min(text.length, match.index + match[0].length + 100);\n const surrounding = text.slice(start, end);\n if (phoneContextWords.test(surrounding)) {\n contextValidatedPhones.push(match[0]);\n }\n }\n }\n\n const uniquePhones = [...new Set(contextValidatedPhones.map(p => p.replace(/\\D/g, '')))];\n if (uniquePhones.length === 1) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Single consistent phone number found' });\n } else if (uniquePhones.length > 1) {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple phone numbers found (${uniquePhones.length})`, fix: 'Use one primary phone number consistently across all pages' });\n } else {\n findings.push({ severity: 'low', detail: 'No phone number found on homepage' });\n score += 1;\n }\n\n // Check for address\n const hasAddress = /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text);\n if (hasAddress) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Physical address found on page' });\n }\n\n // Check for Organization schema\n const hasOrgSchema = /organization|localbusiness/i.test(html);\n if (hasOrgSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Organization/LocalBusiness schema reinforces entity identity' });\n } else {\n findings.push({ severity: 'high', detail: 'No Organization schema to reinforce entity identity', fix: 'Add Organization JSON-LD with consistent name, address, phone, and social links' });\n }\n\n // Check for social proof\n const hasSameAs = /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html);\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Social media / sameAs references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No social media links or sameAs found', fix: 'Add sameAs links in Organization schema to social profiles' });\n }\n\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 6: robots.txt\nfunction checkRobotsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.robotsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n findings.push({ severity: 'high', detail: 'No robots.txt file found', fix: 'Create a robots.txt that explicitly allows AI crawlers' });\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: 2, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text.toLowerCase();\n let score = 3; // exists\n\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => text.includes(c));\n\n if (mentionedCrawlers.length > 0) {\n score += 3;\n findings.push({ severity: 'info', detail: `AI crawlers mentioned: ${mentionedCrawlers.join(', ')}` });\n\n const blocked = mentionedCrawlers.filter(c => {\n // Extract only this crawler's section (up to next User-agent: or EOF)\n // NOTE: do NOT use 'm' flag here - it makes $ match end-of-line, causing\n // non-greedy [\\s\\S]*? to stop at the first line instead of capturing the full section\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(result.text);\n if (!match) return false;\n const section = match[1];\n // If section has \"Allow: /\", the crawler is explicitly allowed (not blocked)\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n // Check for \"Disallow: /\" (root-only, not /path) within this section\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n if (blocked.length > 0) {\n score -= 2;\n findings.push({ severity: 'critical', detail: `AI crawlers BLOCKED: ${blocked.join(', ')}`, fix: 'Change Disallow: / to Allow: / for AI crawler user-agents' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'AI crawlers are allowed to index the site' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No explicit AI crawler rules in robots.txt', fix: 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot with Allow: /' });\n }\n\n if (text.includes('sitemap:')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Sitemap URL referenced in robots.txt' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap reference in robots.txt', fix: 'Add Sitemap: https://yoursite.com/sitemap.xml to robots.txt' });\n }\n\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P0' };\n}\n\n// Criterion 7: FAQ Section\nfunction checkFAQSection(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n const homepage = data.homepage;\n const hasHomepageFAQ = homepage && /faq|frequently\\s+asked/i.test(homepage.text);\n\n if (hasHomepageFAQ) {\n score += 2;\n findings.push({ severity: 'info', detail: 'FAQ content found on homepage' });\n } else {\n findings.push({ severity: 'low', detail: 'No FAQ content found on homepage', fix: 'Add an FAQ section to your homepage addressing common visitor questions' });\n }\n\n const faqPage = data.faqPage;\n const hasFaqPage = faqPage && faqPage.status === 200 && faqPage.text.length > 500;\n\n if (hasFaqPage) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Dedicated FAQ page exists' });\n\n if (/accordion|toggle|collaps|expand/i.test(faqPage.text)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQ uses accordion/toggle UI pattern' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No dedicated FAQ page found at /faq', fix: 'Create a comprehensive FAQ page at /faq covering common questions about your service' });\n }\n\n // Check for FAQPage schema (homepage + FAQ page + blog sample)\n const blogHtml = getBlogHtml(data);\n const allHtml = (homepage?.text || '') + (faqPage?.text || '') + blogHtml;\n if (/faqpage/i.test(allHtml) && /application\\/ld\\+json/i.test(allHtml)) {\n score += 3;\n const faqOnBlog = blogHtml && /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n findings.push({ severity: 'info', detail: faqOnBlog ? 'FAQPage schema markup found on blog posts' : 'FAQPage schema markup found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema markup', fix: 'Add FAQPage JSON-LD schema to pages with FAQ content' });\n }\n\n const questionCount = (allHtml.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>/gi) || []).length;\n if (questionCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${questionCount} question headings found across checked pages` });\n } else if (questionCount >= 5) {\n findings.push({ severity: 'low', detail: `Only ${questionCount} question headings found`, fix: 'Expand FAQ to cover at least 10-15 common questions' });\n }\n\n return { criterion: 'faq_section', criterion_label: 'Comprehensive FAQ Sections', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 8: Original Data\nfunction checkOriginalData(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze homepage + all blog sample pages for original data signals\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const html = data.homepage.text;\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Stats check: strong (+3) if research context nearby, weak (+1) for generic marketing stats\n const statPatterns = /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i;\n if (statPatterns.test(allText)) {\n const researchContext = /\\b(our\\s+(?:study|analysis|research|data|survey|findings|report)|we\\s+(?:surveyed|analyzed|studied|measured|tracked)|proprietary|methodology|original\\s+research)\\b/i;\n if (researchContext.test(allText)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Proprietary statistics with research context found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Statistics found but without research context (e.g., \"500+ clients\")', fix: 'Add context about your methodology: \"Our analysis of X found...\" or \"We surveyed Y...\"' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No proprietary data or statistics found', fix: 'Add unique statistics, case study results, or industry data that LLMs would cite as authoritative' });\n }\n\n // Case studies: with nearby metric (+3), without metric (+1)\n const caseStudyPattern = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i;\n if (caseStudyPattern.test(text)) {\n // Check for a numeric metric within ~200 chars of the case study mention\n const caseStudyRegex = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/gi;\n let hasMetricNearby = false;\n let csMatch: RegExpExecArray | null;\n while ((csMatch = caseStudyRegex.exec(text)) !== null) {\n const start = Math.max(0, csMatch.index - 200);\n const end = Math.min(text.length, csMatch.index + csMatch[0].length + 200);\n const surrounding = text.slice(start, end);\n if (/\\d+%|\\$[\\d,]+|\\d+x\\b/i.test(surrounding)) {\n hasMetricNearby = true;\n break;\n }\n }\n if (hasMetricNearby) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials with specific metrics found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Case studies or testimonials mentioned but without specific metrics', fix: 'Add measurable outcomes to case studies (e.g., \"increased traffic by 45%\")' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No case studies or testimonials found', fix: 'Add case studies with specific outcomes and metrics' });\n }\n\n // Expert attribution (+2) - check homepage + blog\n const combinedText = data.blogSample && data.blogSample.length > 0\n ? text + ' ' + data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ')\n : text;\n if (/written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(combinedText)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Expert attribution or credentials found' });\n } else {\n findings.push({ severity: 'low', detail: 'No expert attribution or credentials visible', fix: 'Add author bios with credentials to establish E-E-A-T signals' });\n }\n\n // Blog check: require actual href links to content paths, not just the word \"blog\"\n const contentLinkPattern = /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i;\n if (contentLinkPattern.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Links to blog/articles section found on site' });\n } else {\n findings.push({ severity: 'medium', detail: 'No links to blog or articles section found', fix: 'Create a content section with expert articles and link to it from your homepage' });\n }\n\n // Blog sample enhancement: case studies from blog posts\n if (data.blogSample && data.blogSample.length > 0 && !caseStudyPattern.test(text)) {\n const blogText = data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n if (caseStudyPattern.test(blogText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials found on blog posts' });\n }\n }\n\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P2' };\n}\n\n// Criterion 9: Internal Linking\nfunction checkInternalLinking(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n\n if (internalLinks.length >= 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `${internalLinks.length} internal links found on homepage` });\n } else if (internalLinks.length >= 10) {\n score += 2;\n findings.push({ severity: 'low', detail: `${internalLinks.length} internal links on homepage`, fix: 'Add more internal links to key service/content pages' });\n } else {\n findings.push({ severity: 'high', detail: `Only ${internalLinks.length} internal links on homepage`, fix: 'Add prominent internal links to service pages, FAQ, blog, and about pages' });\n }\n\n if (/breadcrumb|aria-label=\"breadcrumb\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Breadcrumb navigation detected' });\n } else {\n findings.push({ severity: 'medium', detail: 'No breadcrumb navigation found', fix: 'Add breadcrumb navigation with BreadcrumbList schema markup' });\n }\n\n if (/<nav[\\s>]/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Semantic <nav> element used for navigation' });\n } else {\n findings.push({ severity: 'low', detail: 'No semantic <nav> element found', fix: 'Wrap navigation menus in <nav> for better AI and accessibility parsing' });\n }\n\n if (/related|see\\s+also|learn\\s+more|explore|you\\s+may\\s+also/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Related content or cross-linking sections found' });\n } else {\n findings.push({ severity: 'low', detail: 'No related content or cross-linking found', fix: 'Add \"Related Services\" or \"Learn More\" sections to build topic clusters' });\n }\n\n if (/<footer[\\s>]/i.test(html)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Footer element with likely navigation links' });\n } else {\n findings.push({ severity: 'low', detail: 'No <footer> element found', fix: 'Add a <footer> with navigation links, contact info, and site structure' });\n }\n\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 10: Semantic HTML\nfunction checkSemanticHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for semantic element detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n let score = 0;\n\n const checks: [string, RegExp, string][] = [\n ['<main>', /<main[\\s>]/i, 'Wrap primary page content in <main>'],\n ['<article>', /<article[\\s>]/i, 'Use <article> for standalone content blocks'],\n ['<time>', /<time[\\s>]/i, 'Use <time> elements for dates'],\n ['<nav>', /<nav[\\s>]/i, 'Use <nav> for navigation sections'],\n ['<header>', /<header[\\s>]/i, 'Use <header> for page/section headers'],\n ['<footer>', /<footer[\\s>]/i, 'Use <footer> for page/section footers'],\n ];\n\n let found = 0;\n for (const [name, regex, fix] of checks) {\n if (regex.test(combinedHtml)) {\n found++;\n } else {\n findings.push({ severity: 'low', detail: `Missing ${name} element`, fix });\n }\n }\n score += Math.min(4, Math.floor(found * 0.7));\n if (found >= 4) findings.push({ severity: 'info', detail: `${found}/6 key semantic HTML5 elements found` });\n\n // Check img alt text\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n if (images.length > 0) {\n const ratio = imagesWithAlt.length / images.length;\n if (ratio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(ratio * 100)}% of images have alt text` });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${Math.round(ratio * 100)}% of images have alt text`, fix: 'Add descriptive alt text to all images' });\n }\n }\n\n // Check lang attribute\n if (/lang=\"[a-z]{2}\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'HTML lang attribute set' });\n } else {\n findings.push({ severity: 'medium', detail: 'Missing lang attribute on <html> tag', fix: 'Add lang=\"en\" (or appropriate language) to the <html> tag' });\n }\n\n // Check for ARIA roles\n if (/role=\"|aria-/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'ARIA attributes found for accessibility' });\n } else {\n findings.push({ severity: 'low', detail: 'No ARIA roles or attributes found', fix: 'Add ARIA roles and labels to improve accessibility and semantic parsing' });\n }\n\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 11: Content Freshness\nfunction checkContentFreshness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // JSON-LD date signals (datePublished, dateModified)\n const hasDatePublished = /datePublished|dateCreated/i.test(html);\n const hasDateModified = /dateModified/i.test(html);\n if (hasDatePublished || hasDateModified) {\n score += 3;\n findings.push({ severity: 'info', detail: `JSON-LD date properties found: ${[hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'high', detail: 'No JSON-LD date properties (datePublished/dateModified) found', fix: 'Add datePublished and dateModified to Article or WebPage schema' });\n }\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${timeElements.length} <time> elements found` });\n } else if (timeElements.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 <time> element found', fix: 'Use <time datetime=\"...\"> for all dates to help AI parsers' });\n } else {\n findings.push({ severity: 'medium', detail: 'No <time> elements found', fix: 'Wrap publication and modification dates in <time datetime=\"...\"> elements' });\n }\n\n // Article meta (article:published_time, article:modified_time)\n const hasArticleMeta = /article:published_time|article:modified_time/i.test(html);\n if (hasArticleMeta) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Open Graph article date meta tags found' });\n } else {\n findings.push({ severity: 'low', detail: 'No article:published_time or article:modified_time meta tags', fix: 'Add Open Graph article date meta tags' });\n }\n\n // Recency check - look for recent year references\n const currentYear = new Date().getFullYear();\n const hasRecentYear = html.includes(String(currentYear)) || html.includes(String(currentYear - 1));\n if (hasRecentYear) {\n score += 2;\n findings.push({ severity: 'info', detail: `References to ${currentYear} or ${currentYear - 1} found, suggesting recent content` });\n } else {\n findings.push({ severity: 'low', detail: 'No references to recent years found on homepage' });\n }\n\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 12: Sitemap Completeness\nfunction checkSitemapCompleteness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'critical', detail: 'No sitemap.xml found', fix: 'Create a sitemap.xml with all indexable pages and submit to search engines' });\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const text = sitemap.text;\n let score = 2; // exists\n\n // Valid XML structure\n if (text.includes('<urlset') || text.includes('<sitemapindex')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Valid sitemap XML structure detected' });\n } else {\n findings.push({ severity: 'high', detail: 'sitemap.xml does not contain valid XML structure', fix: 'Ensure sitemap uses proper <urlset> or <sitemapindex> XML format' });\n }\n\n // Count URLs\n const urlCount = (text.match(/<loc>/gi) || []).length;\n if (urlCount >= 50) {\n score += 3;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount >= 10) {\n score += 2;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${urlCount} URL(s) in sitemap`, fix: 'Add all important pages to your sitemap' });\n }\n\n // Recent lastmod dates\n const lastmodMatches = text.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length > 0) {\n score += 2;\n findings.push({ severity: 'info', detail: `${lastmodMatches.length} URLs have lastmod dates` });\n } else {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add <lastmod> dates to sitemap entries for freshness signals' });\n }\n\n // Sitemap index\n if (text.includes('<sitemapindex')) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Sitemap index found, indicating organized sitemap structure' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap index structure', fix: 'Use a <sitemapindex> with multiple child sitemaps for larger sites to improve crawl efficiency' });\n }\n\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 13: RSS Feed\nfunction checkRssFeed(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // Check for RSS link tag in homepage\n const hasRssLink = data.homepage && /<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"/i.test(data.homepage.text);\n if (hasRssLink) {\n score += 3;\n findings.push({ severity: 'info', detail: 'RSS/Atom feed link tag found in homepage <head>' });\n } else {\n findings.push({ severity: 'high', detail: 'No RSS/Atom feed link tag in homepage', fix: 'Add <link rel=\"alternate\" type=\"application/rss+xml\" href=\"/feed\"> to your <head>' });\n }\n\n // Check for valid feed content\n const feed = data.rssFeed;\n if (feed && feed.status === 200) {\n const feedText = feed.text;\n const isValidFeed = feedText.includes('<rss') || feedText.includes('<feed') || feedText.includes('<channel');\n if (isValidFeed) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Valid RSS/Atom feed content detected' });\n\n // Count items\n const itemCount = (feedText.match(/<item[\\s>]|<entry[\\s>]/gi) || []).length;\n if (itemCount >= 5) {\n score += 4;\n findings.push({ severity: 'info', detail: `Feed contains ${itemCount} items` });\n } else if (itemCount > 0) {\n score += 2;\n findings.push({ severity: 'low', detail: `Feed contains only ${itemCount} item(s)`, fix: 'Publish more content to populate your RSS feed with at least 5 items' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'Feed URL returned content but not valid RSS/Atom XML', fix: 'Ensure your feed outputs valid RSS 2.0 or Atom XML' });\n }\n } else if (!hasRssLink) {\n findings.push({ severity: 'medium', detail: 'No accessible RSS/Atom feed found', fix: 'Create an RSS feed to help AI engines discover and index new content automatically' });\n }\n\n return { criterion: 'rss_feed', criterion_label: 'RSS/Atom Feed', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 14: Table & List Extractability\nfunction checkTableListExtractability(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML so blog tables/lists count\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Tables with headers\n const tables = html.match(/<table[\\s>]/gi) || [];\n const tablesWithHeaders = (html.match(/<table[\\s\\S]*?<\\/table>/gi) || []).filter(t => /<th[\\s>]/i.test(t));\n if (tablesWithHeaders.length >= 1) {\n score += 3;\n findings.push({ severity: 'info', detail: `${tablesWithHeaders.length} table(s) with <th> headers found` });\n } else if (tables.length > 0) {\n score += 1;\n findings.push({ severity: 'medium', detail: `${tables.length} table(s) found but without <th> header cells`, fix: 'Add <th> header cells to tables for better AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No HTML tables found', fix: 'Use comparison tables with <th> headers for structured data AI engines can extract' });\n }\n if (tablesWithHeaders.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Multiple well-structured tables present' });\n } else if (tablesWithHeaders.length === 1) {\n findings.push({ severity: 'low', detail: 'Only 1 table with headers found', fix: 'Add more comparison or data tables with <th> headers to increase extractable structured content' });\n }\n\n // Ordered lists\n const olCount = (html.match(/<ol[\\s>]/gi) || []).length;\n if (olCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${olCount} ordered list(s) found - good for step-by-step content` });\n } else {\n findings.push({ severity: 'low', detail: 'No ordered lists (<ol>) found', fix: 'Use <ol> for sequential content (steps, rankings, processes)' });\n }\n\n // Unordered lists\n const ulCount = (html.match(/<ul[\\s>]/gi) || []).length;\n if (ulCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${ulCount} unordered list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No unordered lists (<ul>) found', fix: 'Use <ul> for feature lists, benefits, and bullet-point content' });\n }\n\n // List items count\n const liCount = (html.match(/<li[\\s>]/gi) || []).length;\n if (liCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${liCount} list items - substantial extractable content` });\n }\n\n // Definition lists\n const dlCount = (html.match(/<dl[\\s>]/gi) || []).length;\n if (dlCount >= 1) {\n score += 1;\n findings.push({ severity: 'info', detail: `${dlCount} definition list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No definition lists (<dl>) found', fix: 'Use <dl>/<dt>/<dd> for term-definition pairs to improve AI extractability' });\n }\n\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 15: Definition Patterns\nfunction checkDefinitionPatterns(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for definition pattern detection\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const html = combinedHtml;\n let score = 0;\n\n // \"X is\" / \"X are\" / \"X refers to\" / \"defined as\" patterns\n const definitionPatterns = [\n /\\b\\w[\\w\\s]{2,30}\\bis\\s+(?:a|an|the)\\s/gi,\n /\\b\\w[\\w\\s]{2,30}\\bare\\s+(?:a|an|the)\\s/gi,\n /\\brefers?\\s+to\\b/gi,\n /\\bdefined\\s+as\\b/gi,\n /\\bknown\\s+as\\b/gi,\n /\\bmeans?\\s+that\\b/gi,\n ];\n\n let patternCount = 0;\n for (const pattern of definitionPatterns) {\n const matches = text.match(pattern) || [];\n patternCount += matches.length;\n }\n\n if (patternCount >= 3) {\n score += 5;\n findings.push({ severity: 'info', detail: `${patternCount} definition-style patterns found (e.g., \"X is a...\", \"refers to\", \"defined as\")` });\n } else if (patternCount >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `Only ${patternCount} definition pattern(s) found`, fix: 'Start key descriptions with clear definition patterns like \"X is a...\" or \"X refers to...\"' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition patterns found', fix: 'Add clear definitions using patterns like \"[Term] is [definition]\" that AI engines can extract as snippets' });\n }\n\n // Early placement (in first 2000 chars of text content)\n const earlyText = text.slice(0, 2000);\n const earlyDefinitions = definitionPatterns.some(p => p.test(earlyText));\n // Reset lastIndex since we used global flags\n definitionPatterns.forEach(p => { p.lastIndex = 0; });\n if (earlyDefinitions) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Definition patterns appear early in page content - good for snippet extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No definition patterns in the first 2000 characters of content', fix: 'Place key definitions early on the page where AI engines prioritize extraction' });\n }\n\n // <dfn> or <abbr> usage\n const hasDfn = /<dfn[\\s>]/i.test(html);\n const hasAbbr = /<abbr[\\s>]/i.test(html);\n if (hasDfn || hasAbbr) {\n score += 1;\n findings.push({ severity: 'info', detail: `Semantic definition elements found: ${[hasDfn && '<dfn>', hasAbbr && '<abbr>'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'low', detail: 'No <dfn> or <abbr> elements found', fix: 'Use <dfn> for term definitions and <abbr> for abbreviations to help AI parse terminology' });\n }\n\n // Glossary or definition-list patterns\n if (/<dl[\\s>]/i.test(html) || /glossary|definitions|terminology/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Glossary or definition list structure detected' });\n } else {\n findings.push({ severity: 'low', detail: 'No glossary or definition list found', fix: 'Add a glossary section using <dl>/<dt>/<dd> for key industry terms' });\n }\n\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 16: Direct Answer Density\nfunction checkDirectAnswerDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n // Use combined HTML for Q&A pair and paragraph detection\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Q&A pairs: question heading followed by paragraph\n const qaPairs = html.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>\\s*<p[^>]*>/gi) || [];\n if (qaPairs.length >= 3) {\n score += 6;\n findings.push({ severity: 'info', detail: `${qaPairs.length} question-answer pairs found (question heading + direct answer paragraph)` });\n } else if (qaPairs.length >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `${qaPairs.length} question-answer pair(s) found`, fix: 'Add more question headings (H2/H3) immediately followed by concise answer paragraphs' });\n } else {\n findings.push({ severity: 'high', detail: 'No direct question-answer pairs found', fix: 'Structure content with question headings (e.g., \"What is X?\") immediately followed by a concise answer paragraph' });\n }\n\n // Snippet-zone paragraphs (40-150 words - ideal for AI extraction)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n const snippetZoneParagraphs = paragraphs.filter(p => {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = text.split(/\\s+/).length;\n return wordCount >= 40 && wordCount <= 150;\n });\n if (snippetZoneParagraphs.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${snippetZoneParagraphs.length} paragraphs in snippet zone (40-150 words) - ideal for AI extraction` });\n } else if (snippetZoneParagraphs.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${snippetZoneParagraphs.length} paragraph(s) in optimal snippet length`, fix: 'Write more paragraphs in the 40-150 word range for AI snippet extraction' });\n } else {\n findings.push({ severity: 'medium', detail: 'No paragraphs in the optimal snippet zone (40-150 words)', fix: 'Write self-contained paragraphs of 40-150 words that directly answer common questions' });\n }\n\n // Direct answer openers\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const directOpeners = /\\b(yes|no|the answer is|in short|simply put|to summarize)\\b/gi;\n const openerCount = (text.match(directOpeners) || []).length;\n if (openerCount >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Direct answer openers found (e.g., \"Yes,\", \"In short,\")' });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no direct answer openers found', fix: 'Start answers with direct phrases like \"Yes,\", \"No,\", \"In short,\" to signal definitive answers to AI engines' });\n }\n\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 17: Content Licensing\nfunction checkContentLicensing(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // ai.txt exists (ignore HTML catch-all responses)\n const aiTxt = data.aiTxt;\n if (aiTxt && aiTxt.status === 200 && aiTxt.text.length > 20 && !isHtmlResponse(aiTxt)) {\n score += 4;\n findings.push({ severity: 'info', detail: `ai.txt file found (${aiTxt.text.length} characters)` });\n } else {\n findings.push({ severity: 'high', detail: 'No ai.txt file found', fix: 'Create /ai.txt to declare your AI usage policy and content permissions for AI crawlers' });\n }\n\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Policy language on page\n const hasPolicyLanguage = /content\\s+policy|terms\\s+of\\s+use|usage\\s+rights|permission|copyright\\s+policy|licensing|creative\\s+commons/i.test(text);\n if (hasPolicyLanguage) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Content policy or licensing language found on page' });\n } else {\n findings.push({ severity: 'low', detail: 'No content policy or licensing language visible', fix: 'Add clear content usage terms or licensing information' });\n }\n\n // Schema license property\n const hasLicenseSchema = /license|copyrightHolder|copyrightYear/i.test(html) && /application\\/ld\\+json/i.test(html);\n if (hasLicenseSchema) {\n score += 2;\n findings.push({ severity: 'info', detail: 'License or copyright properties found in schema markup' });\n } else {\n findings.push({ severity: 'low', detail: 'No license or copyright properties in schema', fix: 'Add license, copyrightHolder, and copyrightYear to your schema markup' });\n }\n\n // TDM (Text and Data Mining) or Creative Commons\n const hasTdmOrCC = /tdm|text\\s+and\\s+data\\s+mining|creative\\s+commons|CC\\s+BY|creativecommons\\.org/i.test(html + (aiTxt?.text || ''));\n if (hasTdmOrCC) {\n score += 2;\n findings.push({ severity: 'info', detail: 'TDM or Creative Commons licensing references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No TDM or Creative Commons licensing references found', fix: 'Add Text and Data Mining (TDM) permissions or Creative Commons licensing to signal AI-friendly content use' });\n }\n\n return { criterion: 'content_licensing', criterion_label: 'Content Licensing & AI Permissions', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 18: Author Schema Depth\nfunction checkAuthorSchemaDepth(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for author detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Person schema (check combined - blog posts often have author schema)\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(combinedHtml);\n if (hasPersonSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Person schema found in JSON-LD' });\n } else {\n findings.push({ severity: 'medium', detail: 'No Person schema found', fix: 'Add Person schema for authors with name, jobTitle, knowsAbout, and sameAs properties' });\n }\n\n // jobTitle or knowsAbout properties\n const hasJobTitle = /jobTitle|knowsAbout|expertise|hasCredential/i.test(combinedHtml);\n if (hasJobTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author credential properties found (jobTitle/knowsAbout)' });\n } else {\n findings.push({ severity: 'low', detail: 'No jobTitle or knowsAbout in author schema', fix: 'Add jobTitle and knowsAbout to Person schema to establish expertise' });\n }\n\n // sameAs links (social profiles)\n const hasSameAs = /sameAs/i.test(combinedHtml) && hasPersonSchema;\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author sameAs social profile links found' });\n } else {\n findings.push({ severity: 'low', detail: 'No sameAs links to author social profiles', fix: 'Add sameAs URLs (LinkedIn, GitHub) to Person schema to strengthen entity connections' });\n }\n\n // Visible byline\n const hasByline = /written\\s+by|authored?\\s+by|by\\s+[A-Z][a-z]+\\s+[A-Z]/i.test(text) ||\n /class=\"[^\"]*author[^\"]*\"/i.test(html) ||\n /rel=\"author\"/i.test(html);\n if (hasByline) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Visible author byline or attribution found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No visible author byline found', fix: 'Add visible author names with credentials to establish E-E-A-T' });\n }\n\n // <address> element for contact\n if (/<address[\\s>]/i.test(combinedHtml)) {\n score += 1;\n findings.push({ severity: 'info', detail: '<address> element found for contact information' });\n } else {\n findings.push({ severity: 'low', detail: 'No <address> element found for contact information', fix: 'Add an <address> element with contact details to reinforce entity identity' });\n }\n\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 19: Fact Density\nfunction checkFactDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze across homepage + all blog sample pages for fact density\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = allText;\n const pageCount = allPages.length;\n let score = 0;\n\n // Numeric data points (percentages, money, counts)\n const dataPoints = text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD|\\s*EUR)/g) || [];\n const countPhrases = text.match(/\\d+(?:,\\d{3})*\\+?\\s+(?:users?|clients?|customers?|companies|businesses|patients?|members?|employees?|projects?|downloads?)/gi) || [];\n const totalDataPoints = dataPoints.length + countPhrases.length;\n const avgPerPage = pageCount > 0 ? totalDataPoints / pageCount : 0;\n\n if (avgPerPage >= 4) {\n score += 5;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages (avg ${avgPerPage.toFixed(1)}/page)` });\n } else if (avgPerPage >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages` });\n } else if (totalDataPoints >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${totalDataPoints} quantitative data point(s) found across ${pageCount} pages`, fix: 'Add more specific numbers, percentages, and metrics to strengthen credibility' });\n } else {\n findings.push({ severity: 'high', detail: 'No quantitative data points found', fix: 'Add specific statistics (percentages, counts, comparisons) that AI engines can cite' });\n }\n\n // Year references (suggest sourced/dated claims)\n const yearRefs = text.match(/(?:19|20)\\d{2}/g) || [];\n const uniqueYears = [...new Set(yearRefs)];\n if (uniqueYears.length >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueYears.length} different year references found - suggests dated, verifiable claims` });\n } else if (uniqueYears.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 year reference found on page', fix: 'Add more dated references and timestamps to demonstrate current, verifiable information' });\n } else {\n findings.push({ severity: 'low', detail: 'No year references found on page', fix: 'Include specific years and dates to provide verifiable, time-anchored facts' });\n }\n\n // Attribution phrases (source citations)\n const attributions = text.match(/according\\s+to|source:|study\\s+(?:by|from)|research\\s+(?:by|from|shows)|data\\s+from|report\\s+(?:by|from)|published\\s+(?:by|in)/gi) || [];\n if (attributions.length >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${attributions.length} source attribution(s) found (e.g., \"according to\", \"study by\")` });\n } else {\n findings.push({ severity: 'low', detail: 'No source attributions found', fix: 'Add citations like \"According to [source]\" or \"Research from [org] shows\" for credibility' });\n }\n\n // Units of measurement\n const units = text.match(/\\d+\\s*(?:hours?|minutes?|days?|weeks?|months?|years?|miles?|km|lbs?|kg|mg|sq\\s*ft|acres?|gallons?|liters?)/gi) || [];\n if (units.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: `${units.length} measurement units found (hours, miles, etc.) - adds factual precision` });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no units of measurement found', fix: 'Include specific measurements (hours, miles, sq ft, etc.) to add factual precision AI engines can extract' });\n }\n\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 20: Canonical URL\nfunction checkCanonicalUrl(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Canonical link present (handle either attribute order: rel before href OR href before rel)\n const canonicalMatch = html.match(/<link[^>]*rel=\"canonical\"[^>]*href=\"([^\"]*)\"[^>]*>/i)\n || html.match(/<link[^>]*href=\"([^\"]*)\"[^>]*rel=\"canonical\"[^>]*>/i);\n if (canonicalMatch) {\n score += 4;\n findings.push({ severity: 'info', detail: `Canonical URL found: ${canonicalMatch[1].slice(0, 80)}` });\n\n // Self-referencing (canonical points to the same domain)\n const canonicalUrl = canonicalMatch[1];\n if (canonicalUrl.includes(data.domain)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Canonical URL is self-referencing (points to same domain)' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL points to a different domain', fix: 'Ensure canonical URL points to the authoritative version of this page' });\n }\n\n // HTTPS canonical\n if (canonicalUrl.startsWith('https://')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Canonical URL uses HTTPS' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL does not use HTTPS', fix: 'Update canonical URL to use https://' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No canonical URL tag found', fix: 'Add <link rel=\"canonical\" href=\"https://yoursite.com/page\"> to prevent duplicate content issues' });\n }\n\n // Check for duplicate canonical tags (match either attribute order)\n const allCanonicals = html.match(/<link[^>]*(?:rel=\"canonical\"|rel='canonical')[^>]*>/gi) || [];\n if (allCanonicals.length > 1) {\n score -= 1;\n findings.push({ severity: 'high', detail: `${allCanonicals.length} canonical tags found - must have exactly one`, fix: 'Remove duplicate canonical tags, keeping only one per page' });\n } else if (allCanonicals.length === 1) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Single canonical tag present (no duplicates)' });\n }\n\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: Math.max(0, Math.min(10, score)), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Helper: analyze sitemap lastmod dates for content velocity ──────────────\n\nexport interface SitemapDateAnalysis {\n recentCount: number;\n isUniform: boolean;\n uniformDetail?: string;\n totalWithDates: number;\n distinctRecentDays: number;\n}\n\nexport function countRecentSitemapDates(sitemapText: string): SitemapDateAnalysis {\n const lastmodMatches = sitemapText.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length === 0) {\n return { recentCount: 0, isUniform: false, totalWithDates: 0, distinctRecentDays: 0 };\n }\n\n const now = new Date();\n const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n\n // Parse all dates and group by day\n const dayCounts: Record<string, number> = {};\n let recentCount = 0;\n const recentDays = new Set<string>();\n\n for (const match of lastmodMatches) {\n const dateStr = match.replace(/<\\/?lastmod>/gi, '').trim();\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) continue;\n\n const dayKey = date.toISOString().split('T')[0];\n dayCounts[dayKey] = (dayCounts[dayKey] || 0) + 1;\n\n if (date >= ninetyDaysAgo) {\n recentCount++;\n recentDays.add(dayKey);\n }\n }\n\n // Detect uniform pattern: if most common day has >80% of all dated URLs\n const totalWithDates = Object.values(dayCounts).reduce((a, b) => a + b, 0);\n const maxDayCount = Math.max(...Object.values(dayCounts));\n const isUniform = totalWithDates >= 5 && maxDayCount / totalWithDates > 0.8;\n\n let uniformDetail: string | undefined;\n if (isUniform) {\n const topDay = Object.entries(dayCounts).find(([, count]) => count === maxDayCount)![0];\n uniformDetail = `${maxDayCount} of ${totalWithDates} URLs share lastmod date ${topDay} - likely auto-generated by build system`;\n }\n\n return {\n recentCount,\n isUniform,\n uniformDetail,\n totalWithDates,\n distinctRecentDays: recentDays.size,\n };\n}\n\n// ─── Blog URL extraction from sitemap ────────────────────────────────────────\n\nconst BLOG_PATH_PATTERNS = /\\/(?:[^/]*-?)?(?:blog|articles?|insights?|guides?|resources?|news|posts?|learn|help|how-?to|tutorials?|case-stud|whitepapers?)\\b/i;\n\nconst EXCLUDE_PATH_PATTERNS = /\\/(?:tag|category|author|page|feed|wp-content|wp-admin|wp-json|cart|checkout|login|search|api|static|assets|_next)\\b/i;\n\n/**\n * Extract blog/content URLs from a sitemap XML string.\n * Includes URLs matching common blog path patterns, plus deep paths (2+ segments).\n * Sorts by lastmod descending (newest first), returns top N.\n */\nexport function extractBlogUrlsFromSitemap(sitemapText: string, domain: string, limit: number = 50): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const candidates: { url: string; lastmod: string }[] = [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n // Must be same domain\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n // Skip root URL\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n\n // Exclude unwanted paths\n if (EXCLUDE_PATH_PATTERNS.test(path)) continue;\n\n // Include blog-like paths OR deep paths (2+ segments)\n const segments = path.split('/').filter(Boolean);\n const isBlogPath = BLOG_PATH_PATTERNS.test(path);\n const isDeepPath = segments.length >= 2;\n\n if (!isBlogPath && !isDeepPath) continue;\n } catch {\n continue;\n }\n\n const lastmodMatch = block.match(/<lastmod>([^<]+)<\\/lastmod>/i);\n const lastmod = lastmodMatch ? lastmodMatch[1].trim() : '';\n\n candidates.push({ url, lastmod });\n }\n\n // Sort by lastmod descending (newest first), URLs without lastmod go last\n candidates.sort((a, b) => {\n if (a.lastmod && b.lastmod) return b.lastmod.localeCompare(a.lastmod);\n if (a.lastmod) return -1;\n if (b.lastmod) return 1;\n return 0;\n });\n\n return candidates.slice(0, limit).map(c => c.url);\n}\n\n/**\n * Extract the best sub-sitemap URL from a sitemapindex.\n * WordPress often uses sitemapindex pointing to post-sitemap.xml, page-sitemap.xml, etc.\n * Returns null if not a sitemapindex.\n */\nexport function extractSubSitemapUrl(sitemapText: string): string | null {\n if (!sitemapText.includes('<sitemapindex')) return null;\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n if (sitemapLocs.length === 0) return null;\n\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(Boolean);\n\n // Prefer post/blog/article sub-sitemap\n const preferred = urls.find(u => /post|blog|article/i.test(u));\n return preferred || urls[0] || null;\n}\n\n// Criterion 21: Content Velocity\nfunction checkContentVelocity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n let score = 0;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'medium', detail: 'No sitemap available to assess content velocity', fix: 'Create a sitemap.xml with lastmod dates to signal content publishing frequency' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const analysis = countRecentSitemapDates(sitemap.text);\n\n if (analysis.totalWithDates === 0) {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add lastmod dates to sitemap entries to signal content freshness' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 2, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n score += 2;\n findings.push({ severity: 'info', detail: `${analysis.totalWithDates} pages have lastmod dates` });\n\n // Use distinct recent days when lastmod dates are uniform (build-system artifact)\n const effectiveCount = analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n\n if (analysis.isUniform) {\n findings.push({ severity: 'medium', detail: analysis.uniformDetail!, fix: 'Set genuine lastmod dates per page reflecting actual content changes, not build timestamps' });\n }\n\n if (effectiveCount >= 20) {\n score += 8;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - excellent content velocity` });\n } else if (effectiveCount >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - good velocity` });\n } else if (effectiveCount >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days` });\n } else if (effectiveCount >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${effectiveCount} ${analysis.isUniform ? 'distinct date(s)' : 'page(s) updated'} in last 90 days`, fix: 'Publish or update content more frequently to signal active maintenance' });\n } else {\n findings.push({ severity: 'medium', detail: 'No pages updated in the last 90 days', fix: 'Update existing content and publish new pages regularly' });\n }\n\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 22: Schema Coverage (depth across types)\nfunction checkSchemaCoverage(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for schema coverage (blog posts add Article, Person, FAQPage etc.)\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess schema coverage', fix: 'Add JSON-LD schema markup to improve AI engine understanding' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Count distinct schema properties\n const propertyMatches = allSchemaText.match(/\"[a-zA-Z@]+\"\\s*:/g) || [];\n const uniqueProperties = new Set(propertyMatches.map(p => p.replace(/[\":\\s]/g, '').toLowerCase()));\n\n if (uniqueProperties.size >= 15) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties used - rich schema depth` });\n } else if (uniqueProperties.size >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties found` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${uniqueProperties.size} schema properties`, fix: 'Add more properties to your schema types for richer AI understanding' });\n }\n\n // Organization depth (name, url, logo, contactPoint, sameAs, address, etc.)\n const orgProps = ['name', 'url', 'logo', 'contactpoint', 'sameas', 'address', 'telephone', 'description', 'founder', 'foundingdate'];\n const orgPropsFound = orgProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (orgPropsFound.length >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Organization schema has ${orgPropsFound.length}/10 key properties` });\n } else if (orgPropsFound.length >= 3) {\n score += 1;\n findings.push({ severity: 'low', detail: `Organization schema has only ${orgPropsFound.length}/10 key properties`, fix: 'Add more Organization properties: logo, contactPoint, sameAs, address' });\n } else {\n findings.push({ severity: 'medium', detail: `Organization schema has only ${orgPropsFound.length} key properties`, fix: 'Add essential Organization properties: name, url, logo, contactPoint, sameAs, address, telephone' });\n }\n\n // Article schema depth\n const articleProps = ['headline', 'datepublished', 'datemodified', 'author', 'image', 'description', 'publisher'];\n const articlePropsFound = articleProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (articlePropsFound.length >= 4) {\n score += 2;\n findings.push({ severity: 'info', detail: `Article schema has ${articlePropsFound.length}/7 key properties` });\n } else if (articlePropsFound.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Article schema has only ${articlePropsFound.length}/7 key properties`, fix: 'Add headline, datePublished, dateModified, author, image, and publisher to Article schema' });\n } else {\n findings.push({ severity: 'medium', detail: 'Article schema missing or has fewer than 2 key properties', fix: 'Add Article schema with headline, datePublished, author, and publisher properties' });\n }\n\n // @id linking (connected schema graph)\n const hasIdLinking = /\"@id\"\\s*:/i.test(allSchemaText);\n if (hasIdLinking) {\n score += 2;\n findings.push({ severity: 'info', detail: '@id linking found - schema types are connected in a graph' });\n } else {\n findings.push({ severity: 'low', detail: 'No @id linking between schema types', fix: 'Use @id references to connect schema types (e.g., article.publisher -> organization)' });\n }\n\n // Number of distinct types\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person', 'event', 'offer', 'review', 'aboutpage'];\n const foundTypes = schemaTypes.filter(t => allSchemaLower.includes(`\"${t}\"`));\n if (foundTypes.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${foundTypes.length} distinct schema types used: ${foundTypes.join(', ')}` });\n } else if (foundTypes.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${foundTypes.length} distinct schema types used`, fix: 'Add more schema types (FAQPage, BreadcrumbList, Service) for comprehensive AI understanding' });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${foundTypes.length} schema type(s) found - limited coverage`, fix: 'Add multiple schema types (Organization, WebSite, FAQPage, BreadcrumbList) for comprehensive AI understanding' });\n }\n\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 23: Speakable Schema (voice assistant readiness)\nfunction checkSpeakableSchema(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess speakable schema', fix: 'Add JSON-LD schema markup with SpeakableSpecification to indicate voice-readable content sections' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Detect SpeakableSpecification type or \"speakable\" property\n const hasSpeakableType = /speakablespecification/i.test(allSchemaLower);\n const hasSpeakableProperty = /\"speakable\"\\s*:/i.test(allSchemaText);\n const hasSpeakable = hasSpeakableType || hasSpeakableProperty;\n\n if (!hasSpeakable) {\n findings.push({ severity: 'medium', detail: 'No SpeakableSpecification schema found - voice assistants cannot identify readable sections', fix: 'Add SpeakableSpecification schema with cssSelector or xpath targeting key content sections (headlines, summaries, FAQ answers)' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n // Base: speakable detected\n score += 4;\n findings.push({ severity: 'info', detail: 'SpeakableSpecification schema detected - voice assistants can identify readable content' });\n\n // Check for cssSelector or xpath targeting\n const hasCssSelector = /\"cssselector\"/i.test(allSchemaLower);\n const hasXpath = /\"xpath\"/i.test(allSchemaLower);\n if (hasCssSelector || hasXpath) {\n score += 3;\n const targetType = hasCssSelector && hasXpath ? 'cssSelector and xpath' : hasCssSelector ? 'cssSelector' : 'xpath';\n findings.push({ severity: 'info', detail: `Speakable uses ${targetType} targeting for precise content selection` });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema lacks cssSelector or xpath targeting', fix: 'Add cssSelector (e.g., \".article-headline, .article-summary\") or xpath to precisely target speakable sections' });\n }\n\n // Check blog sample coverage\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogHasSpeakable = /speakablespecification/i.test(blogHtml) || /\"speakable\"\\s*:/i.test(blogHtml);\n if (blogHasSpeakable) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Speakable schema also found in blog/content pages - comprehensive voice coverage' });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema only on homepage, not found in blog/content pages', fix: 'Add SpeakableSpecification to article pages to make blog content voice-assistant readable' });\n }\n } else {\n // No blog samples - can't check, give partial credit\n findings.push({ severity: 'info', detail: 'No blog pages sampled - blog speakable coverage not assessed' });\n }\n\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 24: Query-Answer Alignment ────────────────────────────────────\n\n/** Extract question-format headings from HTML */\nfunction extractQuestionHeadings(html: string): string[] {\n const hTags = (html.match(/<h[23][^>]*>([\\s\\S]*?)<\\/h[23]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, '').trim());\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n}\n\nfunction checkQueryAnswerAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for query-answer alignment analysis' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const questionHeadings = extractQuestionHeadings(combinedHtml);\n\n if (questionHeadings.length === 0) {\n findings.push({ severity: 'info', detail: 'No question-format headings (H2/H3) found - scoring neutral', fix: 'Add question-based headings like \"What is...?\", \"How does...?\" to enable Q&A snippet extraction' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n // Split HTML on heading boundaries and check if answer follows each question heading\n let answered = 0;\n for (const qHeading of questionHeadings) {\n try {\n const escapedHeading = qHeading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(escapedHeading + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const match = pattern.exec(combinedHtml);\n if (match) {\n const afterContent = match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n // Check if there's a meaningful answer (at least 20 chars of text content)\n if (afterContent.length >= 20) {\n answered++;\n }\n }\n } catch {\n // Skip headings that produce invalid regex (e.g. from residual script content)\n }\n }\n\n const rate = Math.round((answered / questionHeadings.length) * 100);\n let score: number;\n if (rate >= 80) {\n score = 10;\n findings.push({ severity: 'info', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) followed by direct answers - excellent alignment` });\n } else if (rate >= 50) {\n score = 7;\n findings.push({ severity: 'low', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) have answers`, fix: 'Add concise answer paragraphs after remaining unanswered question headings' });\n } else if (rate > 0) {\n score = 4;\n findings.push({ severity: 'medium', detail: `Only ${answered}/${questionHeadings.length} question headings (${rate}%) are followed by answers`, fix: 'Ensure each question heading is immediately followed by a direct answer paragraph' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${questionHeadings.length} question headings found but none have direct answers`, fix: 'Add answer paragraphs (2-3 sentences) immediately after each question heading' });\n }\n\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 25: Content Cannibalization ───────────────────────────────────\n\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',\n 'of', 'with', 'by', 'from', 'is', 'it', 'as', 'be', 'was', 'are',\n 'this', 'that', 'your', 'our', 'we', 'you', 'how', 'what', 'why',\n]);\n\nfunction extractPageTitle(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const title = titleMatch?.[1]?.trim() || '';\n const h1 = h1Match?.[1]?.replace(/<[^>]*>/g, '').trim() || '';\n return (title + ' ' + h1).toLowerCase().trim();\n}\n\nfunction titleToWordSet(title: string): Set<string> {\n return new Set(\n title.split(/\\s+/).filter(w => w.length > 1 && !STOP_WORDS.has(w))\n );\n}\n\nfunction jaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n let intersection = 0;\n for (const word of a) {\n if (b.has(word)) intersection++;\n }\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction checkContentCannibalization(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for cannibalization analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Collect pages: homepage + all blogSample\n const pages: Array<{ html: string; url: string }> = [\n { html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` },\n ];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Only homepage available - cannot assess content cannibalization', fix: 'Add blog/content pages to enable cross-page topic overlap analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Extract titles and word sets\n const pageTitles = pages.map(p => ({ title: extractPageTitle(p.html), url: p.url }));\n const wordSets = pageTitles.map(p => titleToWordSet(p.title));\n\n // Find site-wide common terms (appearing in >40% of pages).\n // These are \"site theme terms\" (e.g., \"medicare\" on a Medicare site) - NOT cannibalization signals.\n // A focused site SHOULD have many pages sharing core topic terms.\n const termPageCount = new Map<string, number>();\n for (const ws of wordSets) {\n for (const w of ws) {\n termPageCount.set(w, (termPageCount.get(w) || 0) + 1);\n }\n }\n const commonTermThreshold = Math.max(3, pages.length * 0.4);\n const siteThemeTerms = new Set<string>();\n for (const [term, count] of termPageCount) {\n if (count >= commonTermThreshold) siteThemeTerms.add(term);\n }\n\n // Filter word sets: remove site theme terms to compare only distinguishing words\n const filteredSets = wordSets.map(ws => {\n const filtered = new Set<string>();\n for (const w of ws) {\n if (!siteThemeTerms.has(w)) filtered.add(w);\n }\n return filtered;\n });\n\n // Pairwise comparison on FILTERED word sets (only distinguishing terms)\n const cannibalPairs: Array<{ urlA: string; urlB: string; similarity: number }> = [];\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n // Skip pairs where both have empty filtered sets (means both pages are purely about the core topic)\n if (filteredSets[i].size === 0 && filteredSets[j].size === 0) continue;\n const sim = jaccardSimilarity(filteredSets[i], filteredSets[j]);\n if (sim > 0.6) {\n cannibalPairs.push({\n urlA: pageTitles[i].url.slice(0, 60),\n urlB: pageTitles[j].url.slice(0, 60),\n similarity: Math.round(sim * 100),\n });\n }\n }\n }\n\n // Count unique URLs involved in cannibalization (not pairs)\n const cannibalUrls = new Set<string>();\n for (const pair of cannibalPairs) {\n cannibalUrls.add(pair.urlA);\n cannibalUrls.add(pair.urlB);\n }\n // Scale severity by the ratio of cannibalized pages to total pages\n const cannibalRatio = pages.length > 0 ? cannibalUrls.size / pages.length : 0;\n\n let score: number;\n if (cannibalPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no content cannibalization detected` });\n } else if (cannibalRatio <= 0.05) {\n // <5% of pages overlap - negligible\n score = 9;\n findings.push({ severity: 'info', detail: `${cannibalPairs.length} pair(s) of pages with minor topic overlap (${cannibalUrls.size}/${pages.length} pages affected)` });\n } else if (cannibalRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have overlapping topics`, fix: 'Differentiate titles and H1 headings to reduce topic overlap' });\n } else if (cannibalRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for overlapping topics`, fix: 'Consolidate overlapping pages or differentiate their titles and content focus' });\n } else if (cannibalRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have significant content overlap`, fix: 'Many pages compete for the same topics - consolidate or clearly differentiate them' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for the same topics`, fix: 'Severe content cannibalization - consolidate overlapping pages or create clear topic differentiation' });\n }\n\n // Detail findings for up to 3 pairs\n for (const pair of cannibalPairs.slice(0, 3)) {\n findings.push({ severity: 'low', detail: `Overlap (${pair.similarity}%): ${pair.urlA} vs ${pair.urlB}` });\n }\n\n // Cross-criterion adjustment: If topic coherence is very low (<= 4), cap cannibalization score.\n // \"No overlap\" because content is scattered (humidifiers, groceries, neuropathy) is NOT\n // the same as \"no overlap\" because content is well-differentiated within a focused niche.\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && score >= 8) {\n score = 6;\n findings.push({ severity: 'low', detail: 'Low topic overlap but content lacks coherent focus - not a strong signal for AI authority', fix: 'Focus content on fewer core topics to build topical authority that AI engines can identify' });\n }\n\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 26: Visible Date Signal ───────────────────────────────────────\n\nfunction checkVisibleDateSignal(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for date signal analysis' });\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // 1. Visible <time datetime=\"...\">text</time> elements\n const timeElements = combinedHtml.match(/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/gi) || [];\n const hasVisibleTime = timeElements.length > 0;\n if (hasVisibleTime) {\n score += 5;\n findings.push({ severity: 'info', detail: `${timeElements.length} visible <time> element(s) with datetime attribute found` });\n }\n\n // 2. datePublished/dateModified inside JSON-LD <script> blocks only\n const ldJsonBlocks = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const ldJsonText = ldJsonBlocks.join(' ');\n const hasDatePublished = /datePublished/i.test(ldJsonText);\n const hasDateModified = /dateModified/i.test(ldJsonText);\n const hasSchemaDate = hasDatePublished || hasDateModified;\n if (hasSchemaDate) {\n if (!hasVisibleTime) score += 7;\n else score += 5;\n const dateTypes = [hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(' + ');\n findings.push({ severity: 'info', detail: `JSON-LD schema contains ${dateTypes}` });\n }\n\n // 3. article:published_time / article:modified_time meta tags\n const hasMetaPublished = /<meta[^>]*property=\"article:published_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaModified = /<meta[^>]*property=\"article:modified_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaDate = hasMetaPublished || hasMetaModified;\n if (hasMetaDate && !hasVisibleTime && !hasSchemaDate) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Article meta tags with date information found' });\n } else if (hasMetaDate) {\n findings.push({ severity: 'info', detail: 'Article meta date tags also present (supplementary)' });\n }\n\n // Bonus: dateModified within 6 months\n if (hasDateModified) {\n const dateModMatch = ldJsonText.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (dateModMatch) {\n const modDate = new Date(dateModMatch[1]);\n if (!isNaN(modDate.getTime())) {\n const daysDiff = Math.floor((Date.now() - modDate.getTime()) / (1000 * 60 * 60 * 24));\n if (daysDiff <= 180) {\n score += 1;\n findings.push({ severity: 'info', detail: `dateModified is recent (${daysDiff} days ago) - freshness bonus applied` });\n }\n }\n }\n }\n\n score = Math.min(10, score);\n\n if (score === 0) {\n findings.push({ severity: 'high', detail: 'No visible date signals found (no <time> elements, no JSON-LD dates, no article meta dates)', fix: 'Add <time datetime=\"...\"> elements for user-visible dates and datePublished/dateModified to JSON-LD schema' });\n }\n\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Raw data summary extraction (for AI narrative) ─────────────────────────\n\nexport function extractRawDataSummary(data: SiteData): RawDataSummary {\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Schema types\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundSchemaTypes = schemaTypes.filter(t =>\n allSchemaText.includes(`\"${t}\"`) || allSchemaText.includes(`\"@type\":\"${t}\"`)\n );\n\n // Links\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n const externalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('http') && !href.includes(data.domain);\n });\n\n // robots.txt AI crawlers\n const robotsText = (data.robotsTxt?.text || '').toLowerCase();\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => robotsText.includes(c));\n const blockedCrawlers = mentionedCrawlers.filter(c => {\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(data.robotsTxt?.text || '');\n if (!match) return false;\n const section = match[1];\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n\n // Headings\n const hTagContent = (html.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n\n // Images\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n\n // Semantic elements\n const semanticChecks = ['main', 'article', 'nav', 'header', 'footer', 'section', 'time'];\n const foundElements = semanticChecks.filter(el => new RegExp(`<${el}[\\\\s>]`, 'i').test(html));\n\n return {\n domain: data.domain,\n protocol: data.protocol,\n homepage_length: html.length,\n homepage_text_length: text.trim().length,\n has_https: data.protocol === 'https',\n llms_txt_status: data.llmsTxt && !isHtmlResponse(data.llmsTxt) ? data.llmsTxt.status : null,\n llms_txt_length: data.llmsTxt?.status === 200 && !isHtmlResponse(data.llmsTxt) ? (data.llmsTxt.text.length) : 0,\n robots_txt_status: data.robotsTxt && !isHtmlResponse(data.robotsTxt) ? data.robotsTxt.status : null,\n robots_txt_snippet: (data.robotsTxt?.text || '').slice(0, 500),\n robots_txt_ai_crawlers: mentionedCrawlers,\n robots_txt_blocked_crawlers: blockedCrawlers,\n schema_types_found: foundSchemaTypes,\n schema_block_count: ldJsonMatches.length,\n faq_page_status: data.faqPage?.status ?? null,\n faq_page_length: data.faqPage?.status === 200 ? data.faqPage.text.length : 0,\n sitemap_status: data.sitemapXml?.status ?? null,\n internal_link_count: internalLinks.length,\n external_link_count: externalLinks.length,\n question_headings_count: questionHeadings.length,\n h1_count: h1Count,\n has_meta_description: /<meta[^>]*name=\"description\"[^>]*>/i.test(html),\n has_title: /<title[^>]*>[^<]+<\\/title>/i.test(html),\n has_phone: (() => {\n const phoneMatch = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text);\n if (!phoneMatch) return false;\n // Context validation: tel: link, schema telephone, or nearby keywords\n return /href=\"tel:/i.test(html) || /\"telephone\"/i.test(html) ||\n /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i.test(text);\n })(),\n has_address: /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text),\n has_org_schema: /organization|localbusiness/i.test(html),\n has_social_links: /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html),\n semantic_elements_found: foundElements,\n img_count: images.length,\n img_with_alt_count: imagesWithAlt.length,\n has_lang_attr: /lang=\"[a-z]{2}\"/i.test(html),\n has_aria: /role=\"|aria-/i.test(html),\n has_breadcrumbs: /breadcrumb|aria-label=\"breadcrumb\"/i.test(html),\n has_nav: /<nav[\\s>]/i.test(html),\n has_footer: /<footer[\\s>]/i.test(html),\n has_case_studies: /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i.test(text),\n has_statistics: /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i.test(text),\n has_expert_attribution: /written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(text),\n has_blog_section: /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i.test(html),\n // New criteria fields\n has_date_modified_schema: /dateModified/i.test(html),\n time_element_count: (html.match(/<time[\\s>]/gi) || []).length,\n sitemap_url_count: (data.sitemapXml?.text?.match(/<loc>/gi) || []).length,\n has_rss_feed: !!(data.rssFeed && data.rssFeed.status === 200 && !isHtmlResponse(data.rssFeed)),\n table_count: (html.match(/<table[\\s>]/gi) || []).length,\n ordered_list_count: (html.match(/<ol[\\s>]/gi) || []).length,\n unordered_list_count: (html.match(/<ul[\\s>]/gi) || []).length,\n definition_pattern_count: (text.match(/\\brefers?\\s+to\\b|\\bdefined\\s+as\\b|\\bknown\\s+as\\b/gi) || []).length,\n has_ai_txt: !!(data.aiTxt && data.aiTxt.status === 200 && !isHtmlResponse(data.aiTxt)),\n has_person_schema: /\"@type\"\\s*:\\s*\"Person\"/i.test(html),\n fact_data_point_count: (text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD)/g) || []).length,\n has_canonical: /<link[^>]*rel=\"canonical\"/i.test(html),\n has_license_schema: /license|copyrightHolder/i.test(html) && /application\\/ld\\+json/i.test(html),\n sitemap_recent_lastmod_count: (() => {\n const analysis = countRecentSitemapDates(data.sitemapXml?.text || '');\n return analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n })(),\n // Speakable schema fields\n has_speakable_schema: /speakablespecification/i.test(ldJsonMatches.join(' ')) || /\"speakable\"\\s*:/i.test(ldJsonMatches.join(' ')),\n speakable_selector_count: (ldJsonMatches.join(' ').match(/\"cssselector\"|\"xpath\"/gi) || []).length,\n // Blog sample fields\n blog_sample_count: data.blogSample?.length ?? 0,\n blog_sample_urls: data.blogSample?.map(p => p.finalUrl || '').filter(Boolean) ?? [],\n blog_sample_schema_types: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return [];\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogLd = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const blogSchema = blogLd.join(' ').toLowerCase();\n const types = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person'];\n return types.filter(t => blogSchema.includes(`\"${t}\"`));\n })(),\n blog_sample_question_headings: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return 0;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const hTags = (blogHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h)).length;\n })(),\n blog_sample_faq_schema_found: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return false;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n return /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n })(),\n // Criteria 24-26 fields\n question_heading_answer_rate: (() => {\n const combinedHtml = getCombinedHtml(data);\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length === 0) return -1;\n let answered = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim().length >= 20) answered++;\n }\n return Math.round((answered / qHeadings.length) * 100);\n })(),\n question_heading_total: extractQuestionHeadings(getCombinedHtml(data)).length,\n cannibalizing_pairs_count: (() => {\n const pages: Array<{ html: string }> = [{ html: data.homepage?.text || '' }];\n if (data.blogSample) for (const p of data.blogSample.slice(0, 5)) pages.push({ html: p.text });\n if (pages.length <= 1) return 0;\n const ws = pages.map(p => titleToWordSet(extractPageTitle(p.html)));\n let pairs = 0;\n for (let i = 0; i < ws.length; i++) {\n for (let j = i + 1; j < ws.length; j++) {\n if (jaccardSimilarity(ws[i], ws[j]) > 0.6) pairs++;\n }\n }\n return pairs;\n })(),\n page_titles_sampled: 1 + (data.blogSample?.length ?? 0),\n has_visible_date: (/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/i).test(getCombinedHtml(data)),\n has_schema_date_in_ld: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n return /datePublished|dateModified/i.test(ld);\n })(),\n date_modified_recency_days: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n const m = ld.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (!m) return null;\n const d = new Date(m[1]);\n if (isNaN(d.getTime())) return null;\n return Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24));\n })(),\n // Full-crawl stats\n crawl_discovered: data.crawlStats?.discovered ?? 0,\n crawl_fetched: data.crawlStats?.fetched ?? 0,\n crawl_skipped: data.crawlStats?.skipped ?? 0,\n };\n}\n\n// ─── Criterion 27: Topic Coherence ────────────────────────────────────────────\n\n/**\n * Extract meaningful terms from text (strip HTML, tokenize, remove stop words).\n */\nfunction extractTerms(html: string): string[] {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .toLowerCase();\n return text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n}\n\n/**\n * Extract core topic terms from homepage.\n * Uses H1, title tag, and meta description as the strongest signals.\n */\nfunction extractCoreTopicTerms(html: string): Set<string> {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const metaDescMatch = html.match(/<meta\\s+[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']+)[\"']/i)\n || html.match(/<meta\\s+[^>]*content=[\"']([^\"']+)[\"'][^>]*name=[\"']description[\"']/i);\n\n // Combine the most prominent text signals\n const prominentText = [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n metaDescMatch?.[1] || '',\n ].join(' ');\n\n // Also extract all H2 headings from homepage for broader topic context\n const h2Matches = html.match(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi) || [];\n const h2Text = h2Matches.map(h => h.replace(/<[^>]*>/g, '')).join(' ');\n\n const allTerms = extractTerms(prominentText + ' ' + h2Text);\n\n // Count term frequency and take top 25 most common\n const freq = new Map<string, number>();\n for (const t of allTerms) {\n freq.set(t, (freq.get(t) || 0) + 1);\n }\n\n const sorted = [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 25)\n .map(([term]) => term);\n\n return new Set(sorted);\n}\n\n/**\n * Extract title text from a page (title tag + H1 combined).\n */\nfunction getPageTopicText(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n ].join(' ').toLowerCase().trim();\n}\n\n/**\n * Extract bigrams (2-word phrases) from text.\n */\nfunction extractBigrams(text: string): string[] {\n const words = text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const bigrams: string[] = [];\n for (let i = 0; i < words.length - 1; i++) {\n bigrams.push(words[i] + ' ' + words[i + 1]);\n }\n return bigrams;\n}\n\n/**\n * Topic Coherence measures two things:\n * 1. Topic Focus: What % of pages share the dominant topic bigram\n * 2. Topic Scatter: How many distinct topic clusters exist\n *\n * A site with 90% of pages about \"Medicare coverage\" = high coherence\n * A site with pages about Medicare, humidifiers, groceries, neuropathy = low coherence\n */\nfunction checkTopicCoherence(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 0, status: 'not_found', findings, fix_priority: 'P0' };\n }\n\n if (!data.blogSample || data.blogSample.length < 3) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for topic coherence analysis` });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n\n // ─── Extract brand/domain words to exclude from topic analysis ──────────────\n // Compound domain names like \"miramace.com\" need to be split into component words.\n // We do a two-pass approach: first collect raw frequencies, then identify brand terms.\n const domainBase = data.domain.replace(/^www\\./, '').replace(/\\.(com|org|net|io|co|ai)$/i, '').toLowerCase();\n const brandWords = new Set<string>();\n brandWords.add(domainBase);\n // Split on hyphens/underscores\n for (const part of domainBase.split(/[-_]/)) {\n if (part.length > 2) brandWords.add(part);\n }\n\n // ─── Part 1: Topic Focus via dominant terms ─────────────────────────────────\n // First pass: count ALL terms (including potential brand words)\n const rawTermFreq = new Map<string, number>();\n const pageTitleTexts: string[] = [];\n\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n pageTitleTexts.push(topicText);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n rawTermFreq.set(w, (rawTermFreq.get(w) || 0) + 1);\n }\n }\n\n // Second pass: identify brand terms - words that appear in >80% of pages\n // AND are substrings of the domain name (e.g., \"mira\" is in \"miramace\")\n for (const [term, count] of rawTermFreq) {\n if (count / blogPages.length >= 0.80 && domainBase.includes(term)) {\n brandWords.add(term);\n }\n }\n\n // Third pass: rebuild frequencies without brand words\n const termFreq = new Map<string, number>();\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w) && !brandWords.has(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n termFreq.set(w, (termFreq.get(w) || 0) + 1);\n }\n }\n\n // Find the top terms that appear across the most pages\n const sortedTerms = [...termFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topTerm = sortedTerms[0];\n\n // ─── Part 2: Topic Scatter via unique topic clusters ────────────────────────\n // Extract meaningful bigrams from each page title and cluster them\n const bigramFreq = new Map<string, number>();\n const pageBigrams: string[][] = [];\n\n for (const topicText of pageTitleTexts) {\n const bigrams = extractBigrams(topicText)\n .filter(bg => !bg.split(' ').some(w => brandWords.has(w)));\n pageBigrams.push(bigrams);\n const uniqueBigrams = new Set(bigrams);\n for (const bg of uniqueBigrams) {\n bigramFreq.set(bg, (bigramFreq.get(bg) || 0) + 1);\n }\n }\n\n // Find dominant bigram\n const sortedBigrams = [...bigramFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topBigram = sortedBigrams[0];\n\n // Count distinct \"topic clusters\" - bigrams that appear in 2+ pages but are unique subjects\n // A focused site has 1-3 topic clusters; a scattered site has 10+\n const significantBigrams = sortedBigrams.filter(([, count]) => count >= 2);\n // Group overlapping bigrams (sharing a word) into clusters\n const clusterRoots: string[] = [];\n const assigned = new Set<string>();\n for (const [bg] of significantBigrams) {\n if (assigned.has(bg)) continue;\n clusterRoots.push(bg);\n assigned.add(bg);\n const [w1, w2] = bg.split(' ');\n // Mark overlapping bigrams as same cluster\n for (const [otherBg] of significantBigrams) {\n if (assigned.has(otherBg)) continue;\n if (otherBg.includes(w1) || otherBg.includes(w2)) {\n assigned.add(otherBg);\n }\n }\n }\n const topicClusterCount = clusterRoots.length;\n\n // Count pages that contain the dominant term\n const dominantTerm = topTerm?.[0] || '';\n const dominantTermCount = topTerm?.[1] || 0;\n const focusRatio = blogPages.length > 0 ? dominantTermCount / blogPages.length : 0;\n\n // Count pages that contain the dominant bigram\n const dominantBigram = topBigram?.[0] || '';\n const dominantBigramCount = topBigram?.[1] || 0;\n const bigramFocusRatio = blogPages.length > 0 ? dominantBigramCount / blogPages.length : 0;\n\n // ─── Scoring ────────────────────────────────────────────────────────────────\n // Primary signal: what % of pages share the dominant topic term.\n // Secondary signal: how many distinct topic clusters exist.\n // A Medicare site with varied products (\"scooter\", \"wheelchair\", \"walker\") is STILL coherent\n // if 80%+ of pages share \"medicare\" as the core topic.\n\n let score = 0;\n const bestFocusRatio = Math.max(focusRatio, bigramFocusRatio);\n\n // For sites with many pages, absolute term count matters more than ratio.\n // A term appearing on 15 of 60 pages (25%) is better than 2 of 5 pages (40%).\n const dominantPageCount = Math.max(dominantTermCount, dominantBigramCount);\n const hasStrongAbsolutePresence = dominantPageCount >= 10;\n\n // Focus score (0-7 points) - scale thresholds for large sites\n if (bestFocusRatio >= 0.80) {\n score += 7;\n } else if (bestFocusRatio >= 0.60) {\n score += 6;\n } else if (bestFocusRatio >= 0.45 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.30)) {\n score += 5;\n } else if (bestFocusRatio >= 0.30 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.20)) {\n score += 4;\n } else if (bestFocusRatio >= 0.15) {\n score += 2;\n } else {\n score += 1;\n }\n\n // Scatter adjustment (0-3 points for few clusters, penalty for many)\n // Scale cluster thresholds by page count - more pages naturally produce more clusters\n const clusterPenaltyReduced = focusRatio >= 0.70 || hasStrongAbsolutePresence;\n const scaledLow = Math.max(3, Math.floor(blogPages.length / 10));\n const scaledMid = Math.max(6, Math.floor(blogPages.length / 5));\n const scaledHigh = Math.max(10, Math.floor(blogPages.length / 3));\n\n if (topicClusterCount <= scaledLow) {\n score += 3;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic cluster(s) - tightly focused content` });\n } else if (topicClusterCount <= scaledMid) {\n score += clusterPenaltyReduced ? 2 : 1;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters${clusterPenaltyReduced ? ' within a focused niche' : ' - moderately focused'}` });\n } else if (topicClusterCount <= scaledHigh) {\n score += clusterPenaltyReduced ? 1 : 0;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters - scattered content`, fix: 'Reduce the number of distinct topics. Focus blog content on 2-3 core expertise areas.' });\n } else {\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters but strong core topic focus (${Math.round(focusRatio * 100)}%)` });\n }\n } else {\n score += clusterPenaltyReduced ? 0 : -2;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'medium', detail: `${topicClusterCount} topic clusters - highly scattered content`, fix: 'Content covers too many unrelated topics. AI engines cannot identify your expertise. Focus on your core niche.' });\n } else {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters despite strong core topic focus`, fix: 'Consider narrowing subtopics within your niche for even stronger AI visibility.' });\n }\n }\n\n score = Math.max(0, Math.min(10, score));\n\n // Report dominant topic\n if (dominantTerm) {\n const focusPct = Math.round(focusRatio * 100);\n findings.push({ severity: 'info', detail: `Dominant topic term: \"${dominantTerm}\" (${focusPct}% of ${blogPages.length} pages)` });\n }\n if (dominantBigram && dominantBigramCount >= 2) {\n findings.push({ severity: 'info', detail: `Dominant topic phrase: \"${dominantBigram}\" (${dominantBigramCount}/${blogPages.length} pages)` });\n }\n\n // Off-topic examples\n const offTopicExamples: string[] = [];\n for (let i = 0; i < pageTitleTexts.length && offTopicExamples.length < 3; i++) {\n if (dominantTerm && !pageTitleTexts[i].includes(dominantTerm)) {\n const title = blogPages[i].text.match(/<title[^>]*>([^<]+)<\\/title>/i)?.[1]?.trim();\n if (title && title.length > 3) offTopicExamples.push(title.slice(0, 60));\n }\n }\n if (offTopicExamples.length > 0 && score < 8) {\n findings.push({ severity: 'low', detail: `Off-topic examples: ${offTopicExamples.join('; ')}` });\n }\n\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// ─── Criterion 28: Content Depth ──────────────────────────────────────────────\n\nfunction countWords(html: string): number {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\nfunction countHeadings(html: string): number {\n const headings = html.match(/<h[2-6][^>]*>/gi) || [];\n return headings.length;\n}\n\nfunction checkContentDepth(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.blogSample || data.blogSample.length < 2) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for depth analysis` });\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: 3, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n const wordCounts = blogPages.map(p => countWords(p.text));\n const headingCounts = blogPages.map(p => countHeadings(p.text));\n\n const avgWords = wordCounts.reduce((a, b) => a + b, 0) / wordCounts.length;\n const avgHeadings = headingCounts.reduce((a, b) => a + b, 0) / headingCounts.length;\n const deepPages = wordCounts.filter(w => w >= 1000).length;\n const thinPages = wordCounts.filter(w => w < 300).length;\n const deepRatio = deepPages / blogPages.length;\n const thinRatio = thinPages / blogPages.length;\n\n let score = 0;\n\n // Word count scoring (0-5 points)\n if (avgWords >= 2000) {\n score += 5;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - excellent depth` });\n } else if (avgWords >= 1200) {\n score += 4;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - good depth` });\n } else if (avgWords >= 800) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page - moderate depth` });\n } else if (avgWords >= 400) {\n score += 2;\n findings.push({ severity: 'low', detail: `Average ${Math.round(avgWords)} words per page - shallow content`, fix: 'Expand articles with more detail, examples, and expert analysis to build AI citation authority' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Average ${Math.round(avgWords)} words per page - very thin content`, fix: 'Content is too thin for AI engines to cite. Aim for 1000+ words per article with structured sections.' });\n }\n\n // Heading structure scoring (0-3 points)\n if (avgHeadings >= 8) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - well-structured` });\n } else if (avgHeadings >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - decent structure` });\n } else if (avgHeadings >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page`, fix: 'Add more H2/H3 headings to break content into extractable sections' });\n } else {\n findings.push({ severity: 'medium', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - minimal structure`, fix: 'Add question-format H2/H3 headings so AI engines can extract specific answers' });\n }\n\n // Deep content ratio bonus (0-2 points)\n if (deepRatio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages (${Math.round(deepRatio * 100)}%) have 1000+ words` });\n } else if (deepRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages have 1000+ words` });\n }\n\n // Thin content penalty\n if (thinRatio >= 0.5) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: `${thinPages}/${blogPages.length} pages (${Math.round(thinRatio * 100)}%) have under 300 words - high thin content ratio`, fix: 'Remove or expand thin pages. Thin content dilutes site quality for AI engines.' });\n } else if (thinRatio >= 0.25) {\n score = Math.max(0, score - 1);\n findings.push({ severity: 'low', detail: `${thinPages}/${blogPages.length} pages have under 300 words` });\n }\n\n // Cross-criterion: deep content on scattered topics is less valuable for AI authority.\n // A 5000-word article about humidifiers doesn't help a healthcare company's AI citations.\n let finalScore = Math.min(10, score);\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && finalScore >= 8) {\n finalScore = 7;\n findings.push({ severity: 'low', detail: 'Deep content but low topic coherence - depth on scattered topics has reduced AI citation value', fix: 'Focus content depth on your core expertise area for maximum AI visibility' });\n }\n\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: finalScore, status: finalScore >= 7 ? 'pass' : finalScore >= 4 ? 'partial' : 'fail', findings, fix_priority: finalScore >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Main audit function ────────────────────────────────────────────────────\n\n/**\n * Run all 26 criteria checks using pre-fetched site data.\n * All functions are synchronous (no HTTP calls) - data was already fetched.\n */\nexport function auditSiteFromData(data: SiteData): CriterionResult[] {\n // Compute Topic Coherence first - used to cap Content Cannibalization\n const topicCoherence = checkTopicCoherence(data);\n const cannibalization = checkContentCannibalization(data, topicCoherence.score);\n\n return [\n checkLlmsTxt(data),\n checkSchemaMarkup(data),\n checkQAFormat(data),\n checkCleanHTML(data),\n checkEntityConsistency(data),\n checkRobotsTxt(data),\n checkFAQSection(data),\n checkOriginalData(data),\n checkInternalLinking(data),\n checkSemanticHTML(data),\n checkContentFreshness(data),\n checkSitemapCompleteness(data),\n checkRssFeed(data),\n checkTableListExtractability(data),\n checkDefinitionPatterns(data),\n checkDirectAnswerDensity(data),\n checkContentLicensing(data),\n checkAuthorSchemaDepth(data),\n checkFactDensity(data),\n checkCanonicalUrl(data),\n checkContentVelocity(data),\n checkSchemaCoverage(data),\n checkSpeakableSchema(data),\n checkQueryAnswerAlignment(data),\n cannibalization,\n checkVisibleDateSignal(data),\n topicCoherence,\n checkContentDepth(data, topicCoherence.score),\n ];\n}\n\n/**\n * Legacy entry point: fetches data and runs all checks.\n * Used by analyzer.ts for the /api/aeo/analyze endpoint.\n */\nexport async function auditSite(targetUrl: string): Promise<CriterionResult[]> {\n const url = new URL(targetUrl.startsWith('http') ? targetUrl : `https://${targetUrl}`);\n const domain = url.hostname.replace(/^www\\./, '');\n const data = await prefetchSiteData(domain);\n return auditSiteFromData(data);\n}\n","import type { CriterionResult } from './site-crawler.js';\n\nconst WEIGHTS: Record<string, number> = {\n // ─── Content Substance (~55%) ─────────────────────────────────────────────\n // WHY an AI engine would cite you. These drive citation quality directly.\n topic_coherence: 0.14, // Topical authority - THE gating signal\n original_data: 0.10, // Unique value AI can't find elsewhere\n content_depth: 0.07, // Comprehensive vs thin coverage\n fact_density: 0.06, // Information density per page\n direct_answer_density: 0.05, // Direct answers to queries\n qa_content_format: 0.05, // Answer-shaped content structure\n query_answer_alignment: 0.05, // Relevance to actual AI queries\n faq_section: 0.04, // Structured Q&A pairs\n\n // ─── Content Organization (~30%) ──────────────────────────────────────────\n // HOW easily AI engines can extract and trust your content.\n entity_consistency: 0.05, // Brand authority and E-E-A-T\n internal_linking: 0.04, // Site structure and topic clusters\n content_freshness: 0.04, // Recency signals\n schema_markup: 0.03, // Structured data for discovery\n author_schema_depth: 0.03, // Expert attribution\n table_list_extractability: 0.03, // Extractable structured data\n definition_patterns: 0.02, // Clear definitions\n visible_date_signal: 0.02, // Publication date trust\n semantic_html: 0.02, // Clean semantic structure\n clean_html: 0.02, // Parseable markup\n\n // ─── Technical Plumbing (~15%) ────────────────────────────────────────────\n // WHETHER AI crawlers can find you. Table stakes with diminishing returns.\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\nexport function calculateOverallScore(criteria: CriterionResult[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const c of criteria) {\n const weight = WEIGHTS[c.criterion] ?? 0.05;\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n if (totalWeight === 0) return 0;\n\n let score = Math.round(weightedSum / totalWeight);\n\n // ─── Coherence Gate ─────────────────────────────────────────────────────\n // Topic coherence is a multiplicative signal, not just additive. A site\n // with scattered, unfocused content cannot achieve a high AEO score\n // regardless of technical perfection - AI engines won't build topical\n // trust from a grab-bag of unrelated posts.\n //\n // Thresholds map to score tiers:\n // coherence 0-1: max 35-40 (Critical gaps)\n // coherence 2-4: max 45-55 (Below average)\n // coherence 5: max 60 (Moderate ceiling)\n // coherence ≥ 6: no cap\n const coherence = criteria.find(c => c.criterion === 'topic_coherence');\n if (coherence && coherence.score < 6) {\n const cap = 35 + coherence.score * 5;\n score = Math.min(score, cap);\n }\n\n return score;\n}\n","/**\r\n * SPA detection and headless Chromium rendering for pre-crawl.\r\n *\r\n * When a site returns a thin JS-only shell (e.g. React CRA, Vite SPA),\r\n * the regular fetch() gets almost no text content, causing false low scores.\r\n * This module detects those shells and re-renders them with Puppeteer.\r\n */\r\n\r\nimport type { FetchResult } from './site-crawler.js';\r\n\r\n// ─── SPA classification ─────────────────────────────────────────────────────\r\n\r\nexport type RenderingMethod = 'server' | 'client-spa';\r\n\r\ninterface RenderingClassification {\r\n method: RenderingMethod;\r\n framework: string | null;\r\n}\r\n\r\n// ─── SPA shell detection ────────────────────────────────────────────────────\r\n\r\nconst SPA_INDICATORS = [\r\n // Root mount points (empty or nearly empty, including self-closing)\r\n /<div\\s+id=[\"'](root|app|__next|__nuxt|__vue)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i,\r\n // Framework globals\r\n /__NEXT_DATA__/,\r\n /__NUXT__/,\r\n // CRA / Vite bundle patterns\r\n /src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i,\r\n /src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i,\r\n // React markers\r\n /data-reactroot/i,\r\n // Angular\r\n /ng-version/i,\r\n // Noscript JS warnings\r\n /<noscript>[^<]*(?:javascript|enable\\s+js|requires?\\s+javascript)[^<]*<\\/noscript>/i,\r\n];\r\n\r\n/**\r\n * Detect whether raw HTML is a thin SPA shell that needs client-side rendering.\r\n * Both conditions required:\r\n * 1. Visible text content < 500 chars (thin page)\r\n * 2. At least one SPA framework indicator present\r\n */\r\nexport function isSpaShell(html: string): boolean {\r\n // Strip all tags and collapse whitespace to get visible text\r\n const text = html\r\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\r\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\r\n .replace(/<[^>]*>/g, ' ')\r\n .replace(/\\s+/g, ' ')\r\n .trim();\r\n\r\n if (text.length >= 500) return false;\r\n\r\n return SPA_INDICATORS.some((pattern) => pattern.test(html));\r\n}\r\n\r\n/**\r\n * Classify a page's rendering method from its raw (non-headless) HTML.\r\n * Returns the method ('server' | 'client-spa') and detected framework if any.\r\n */\r\nexport function classifyRendering(html: string): RenderingClassification {\r\n if (!isSpaShell(html)) return { method: 'server', framework: null };\r\n\r\n // Detect framework from SPA indicators\r\n const frameworkPatterns: [RegExp, string][] = [\r\n [/__NEXT_DATA__/, 'next'],\r\n [/__NUXT__/, 'nuxt'],\r\n [/<div\\s+id=[\"']__vue[\"']/i, 'vue'],\r\n [/ng-version/i, 'angular'],\r\n [/data-reactroot/i, 'react'],\r\n [/<div\\s+id=[\"'](root|app)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i, 'react'],\r\n [/src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i, 'react'],\r\n [/src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i, 'vite'],\r\n ];\r\n\r\n for (const [pattern, framework] of frameworkPatterns) {\r\n if (pattern.test(html)) return { method: 'client-spa', framework };\r\n }\r\n\r\n return { method: 'client-spa', framework: null };\r\n}\r\n\r\n// ─── Headless Chromium rendering ────────────────────────────────────────────\r\n\r\nexport interface HeadlessOptions {\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Render a URL with headless Chromium and return the fully-rendered HTML.\r\n * Returns null if Puppeteer is not installed or any error occurs.\r\n * The caller should fall back to the raw HTML in that case.\r\n */\r\nexport async function fetchWithHeadless(\r\n url: string,\r\n options?: HeadlessOptions\r\n): Promise<FetchResult | null> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let puppeteer: any;\r\n try {\r\n // Dynamic import - puppeteer is an optional peer dependency\r\n const mod = 'puppeteer';\r\n puppeteer = await import(/* @vite-ignore */ mod);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const timeout = options?.timeout ?? 25000;\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let browser: any = null;\r\n\r\n try {\r\n browser = await puppeteer.launch({\r\n headless: true,\r\n args: [\r\n '--no-sandbox',\r\n '--disable-setuid-sandbox',\r\n '--disable-dev-shm-usage',\r\n '--disable-gpu',\r\n '--single-process',\r\n ],\r\n });\r\n\r\n const page = await browser.newPage();\r\n\r\n // Block heavy resources to speed up rendering\r\n await page.setRequestInterception(true);\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n page.on('request', (req: any) => {\r\n const type = req.resourceType();\r\n if (['image', 'font', 'media', 'stylesheet'].includes(type)) {\r\n req.abort();\r\n } else {\r\n req.continue();\r\n }\r\n });\r\n\r\n await page.setUserAgent('AEO-Visibility-Bot/1.0');\r\n await page.goto(url, { waitUntil: 'networkidle2', timeout });\r\n\r\n // Wait for JS to populate the body with real text\r\n try {\r\n await page.waitForFunction(\r\n 'document.body && document.body.innerText && document.body.innerText.replace(/\\\\s+/g, \" \").trim().length > 100',\r\n { timeout: 5000 }\r\n );\r\n } catch {\r\n // Body text never exceeded 100 chars - still return what we got\r\n }\r\n\r\n const html = await page.content();\r\n const finalUrl = page.url();\r\n\r\n return {\r\n text: html.slice(0, 500000),\r\n status: 200,\r\n finalUrl,\r\n };\r\n } catch {\r\n return null;\r\n } finally {\r\n if (browser) {\r\n try {\r\n await browser.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n }\r\n }\r\n}\r\n","/**\n * Shared scorecard building functions.\n * Extracted from cli/pre-crawl.ts for reuse in instant-audit and other consumers.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { Status, FindingType, Severity, ScoreCardItem, CriterionDetail, DetailedFinding } from './types.js';\nimport type { AuditFinding } from './types.js';\n\n// ─── Criterion label mapping (site-crawler labels -> prompt-standard names) ──\n\nexport const CRITERION_LABELS: Record<string, string> = {\n 'llms.txt File': 'llms.txt File',\n 'Schema.org Structured Data': 'Schema.org Structured Data',\n 'Q&A Content Format': 'Q&A Content Format',\n 'Clean, Crawlable HTML': 'Clean, Crawlable HTML',\n 'Entity Authority & E-E-A-T': 'Entity Authority & NAP Consistency',\n 'robots.txt for AI Crawlers': 'robots.txt for AI Crawlers',\n 'Comprehensive FAQ Sections': 'Comprehensive FAQ Section',\n 'Original Data & Expert Content': 'Original Data & Expert Analysis',\n 'Internal Linking Architecture': 'Internal Linking Structure',\n 'Semantic HTML5 & Accessibility': 'Semantic HTML5 & Accessibility',\n 'Content Freshness Signals': 'Content Freshness Signals',\n 'Sitemap Completeness': 'Sitemap Completeness',\n 'RSS/Atom Feed': 'RSS/Atom Feed',\n 'Table & List Extractability': 'Table & List Extractability',\n 'Definition Patterns': 'Definition Patterns',\n 'Direct Answer Paragraphs': 'Direct Answer Paragraphs',\n 'Content Licensing & AI Permissions': 'Content Licensing & AI Permissions',\n 'Author & Expert Schema': 'Author & Expert Schema',\n 'Fact & Data Density': 'Fact & Data Density',\n 'Canonical URL Strategy': 'Canonical URL Strategy',\n 'Content Publishing Velocity': 'Content Publishing Velocity',\n 'Schema Coverage & Depth': 'Schema Coverage & Depth',\n 'Speakable Schema': 'Speakable Schema',\n 'Query-Answer Alignment': 'Query-Answer Alignment',\n 'Content Cannibalization': 'Content Cannibalization',\n 'Visible Date Signal': 'Visible Date Signal',\n 'Topic Coherence': 'Topic Coherence',\n 'Content Depth': 'Content Depth',\n};\n\n// ─── Score to Status mapping (matches AI prompt: 0=MISSING...8-10=STRONG) ────\n\nexport function scoreToStatus(score: number): Status {\n if (score === 0) return 'MISSING';\n if (score === 1) return 'NEARLY EMPTY';\n if (score === 2) return 'POOR';\n if (score === 3) return 'WEAK';\n if (score <= 5) return 'PARTIAL';\n if (score === 6) return 'MODERATE';\n if (score === 7) return 'GOOD';\n return 'STRONG';\n}\n\n// ─── Finding severity mapping (site-crawler -> audit format) ─────────────────\n\nexport function mapFindingSeverity(severity: AuditFinding['severity']): Severity {\n switch (severity) {\n case 'critical': return 'CRITICAL';\n case 'high': return 'MISSING';\n case 'medium': return 'ADD';\n case 'low': return 'PARTIAL';\n case 'info': return 'WORKING';\n default: return 'PARTIAL';\n }\n}\n\nexport function mapFindingType(severity: AuditFinding['severity'], hasFix: boolean): FindingType {\n if (severity === 'info') return 'Good';\n if (severity === 'critical') return 'Critical';\n if (severity === 'high') return 'Missing';\n if (hasFix) return 'Issue';\n return 'Note';\n}\n\n// ─── Convert CriterionResult to scorecard + detailedFindings format ──────────\n\nexport function buildScorecard(results: CriterionResult[]): ScoreCardItem[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n // Build keyFindings from the most important findings (2-3 sentences)\n const keyParts: string[] = [];\n for (const f of r.findings) {\n if (keyParts.length >= 3) break;\n keyParts.push(f.detail);\n }\n const keyFindings = keyParts.join('. ') + (keyParts.length > 0 && !keyParts[keyParts.length - 1].endsWith('.') ? '.' : '');\n\n return {\n id: i + 1,\n criterion: label,\n score: r.score,\n status: scoreToStatus(r.score),\n keyFindings,\n };\n });\n}\n\nexport function buildDetailedFindings(results: CriterionResult[]): CriterionDetail[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n const rawFindings: DetailedFinding[] = r.findings.map(f => ({\n type: mapFindingType(f.severity, !!f.fix),\n description: f.fix ? `${f.detail}. ${f.fix}` : f.detail,\n severity: mapFindingSeverity(f.severity),\n }));\n\n // Deduplicate findings by description\n const seen = new Set<string>();\n const findings: DetailedFinding[] = [];\n for (const f of rawFindings) {\n if (!seen.has(f.description)) {\n seen.add(f.description);\n findings.push(f);\n }\n }\n\n // Fallback: if a criterion somehow has fewer than 2 findings, add a single specific one\n if (findings.length < 2) {\n if (r.score >= 7) {\n findings.push({ type: 'Good', description: `${label} is well-implemented for AI engine visibility.`, severity: 'WORKING' });\n } else {\n findings.push({ type: 'Note', description: `${label} needs improvement - review specific issues above.`, severity: 'PARTIAL' });\n }\n }\n\n return {\n id: i + 1,\n name: label,\n findings,\n };\n });\n}\n","/**\n * Deterministic narrative generation from scorecard data.\n * Produces verdict, opportunities, pitchNumbers, and bottomLine\n * without any LLM calls - pure template-based generation.\n */\n\nimport type { ScoreCardItem, Deliverable, PitchMetric, ImpactLevel } from './types.js';\nimport type { CriterionResult } from './site-crawler.js';\nimport type { RawDataSummary } from './site-crawler.js';\n\n// ─── Scoring weights (mirrored from scoring.ts for impact calculation) ───────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.05,\n query_answer_alignment: 0.05,\n faq_section: 0.04,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n definition_patterns: 0.02,\n visible_date_signal: 0.02,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\n// ─── Opportunity templates (28 criteria) ─────────────────────────────────────\n\ninterface OpportunityTemplate {\n name: string;\n effort: string;\n description: string;\n}\n\nconst OPPORTUNITY_TEMPLATES: Record<string, OpportunityTemplate> = {\n llms_txt: {\n name: 'Create llms.txt File',\n effort: 'Low',\n description: 'Add a /llms.txt file that describes your site, core services, and key pages in markdown format. This helps AI engines like ChatGPT and Claude understand your site structure and content offerings.',\n },\n schema_markup: {\n name: 'Add Schema.org Structured Data',\n effort: 'Medium',\n description: 'Implement JSON-LD structured data (Organization, Service, Product, FAQPage) on key pages. Schema markup helps AI engines extract and cite your content accurately.',\n },\n qa_content_format: {\n name: 'Restructure Content as Q&A',\n effort: 'Medium',\n description: 'Add question-based headings (H2/H3) throughout your content. Use \"What is...\", \"How does...\", \"Why should...\" patterns that match how users query AI assistants.',\n },\n clean_html: {\n name: 'Fix HTML Structure & Enable HTTPS',\n effort: 'Medium',\n description: 'Ensure clean, well-structured HTML with proper meta tags, semantic elements, and HTTPS. Clean HTML makes your content more parseable by AI crawlers.',\n },\n entity_consistency: {\n name: 'Strengthen Entity Authority (NAP)',\n effort: 'Low',\n description: 'Add Organization schema with consistent name, address, phone (NAP). Include sameAs links to social profiles and authoritative directories to strengthen entity recognition.',\n },\n robots_txt: {\n name: 'Configure robots.txt for AI Crawlers',\n effort: 'Low',\n description: 'Update robots.txt to explicitly allow AI crawlers (GPTBot, ClaudeBot, PerplexityBot). Add a Sitemap directive to help crawlers discover your content.',\n },\n faq_section: {\n name: 'Build Comprehensive FAQ Section',\n effort: 'Medium',\n description: 'Create a dedicated FAQ page with FAQPage schema markup. Cover common questions about your products, services, and industry to become a direct answer source for AI engines.',\n },\n original_data: {\n name: 'Add Original Data & Case Studies',\n effort: 'High',\n description: 'Publish original research, statistics, case studies, or proprietary data that AI engines can cite. Unique data points make your content a primary source rather than a derivative one.',\n },\n internal_linking: {\n name: 'Improve Internal Linking Architecture',\n effort: 'Medium',\n description: 'Strengthen internal linking with descriptive anchor text between related pages. Add breadcrumb navigation and ensure every key page is reachable within 3 clicks from the homepage.',\n },\n semantic_html: {\n name: 'Implement Semantic HTML5',\n effort: 'Low',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure. Add lang attribute and ARIA labels for accessibility.',\n },\n content_freshness: {\n name: 'Add Content Freshness Signals',\n effort: 'Low',\n description: 'Include dateModified schema, visible last-updated dates, and time elements on content pages. Fresh content signals help AI engines prioritize your pages over stale alternatives.',\n },\n sitemap_completeness: {\n name: 'Create Complete Sitemap',\n effort: 'Low',\n description: 'Generate a comprehensive sitemap.xml with lastmod dates for all important pages. A complete sitemap ensures AI crawlers can discover and prioritize your full content library.',\n },\n rss_feed: {\n name: 'Deploy RSS/Atom Feed',\n effort: 'Low',\n description: 'Add an RSS or Atom feed linked from your homepage. Feeds signal active content publishing and give AI engines a structured way to track your latest content.',\n },\n table_list_extractability: {\n name: 'Add Structured Tables & Lists',\n effort: 'Medium',\n description: 'Use HTML tables for comparison data and ordered/unordered lists for features, steps, and specifications. Structured data formats are directly extractable by AI engines for answers.',\n },\n definition_patterns: {\n name: 'Add Definition-Style Content',\n effort: 'Low',\n description: 'Include clear definition patterns (\"X refers to...\", \"X is defined as...\") for key terms and concepts. Definition-style content is highly citable by AI engines answering \"what is\" queries.',\n },\n direct_answer_density: {\n name: 'Add Direct Answer Paragraphs',\n effort: 'Medium',\n description: 'Write concise, standalone answer paragraphs (2-3 sentences) immediately after question headings. These \"snippet-ready\" paragraphs are ideal for AI engine citations.',\n },\n content_licensing: {\n name: 'Add Content Licensing & ai.txt',\n effort: 'Low',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to your structured data. Clear licensing signals help AI engines understand how they can use your content.',\n },\n author_schema_depth: {\n name: 'Enhance Author & Expert Schema',\n effort: 'Low',\n description: 'Add Person schema for content authors with credentials, expertise, and sameAs links. Expert attribution strengthens E-E-A-T signals that AI engines use to evaluate source credibility.',\n },\n fact_density: {\n name: 'Increase Fact & Data Density',\n effort: 'Medium',\n description: 'Add specific numbers, percentages, statistics, and data points throughout your content. Fact-dense content gives AI engines concrete data to cite rather than vague claims.',\n },\n canonical_url: {\n name: 'Fix Canonical URL Strategy',\n effort: 'Low',\n description: 'Add rel=\"canonical\" tags to all pages pointing to the preferred URL version. Canonical URLs prevent duplicate content confusion and consolidate AI engine citations to a single authoritative URL.',\n },\n content_velocity: {\n name: 'Increase Publishing Frequency',\n effort: 'High',\n description: 'Establish a regular content publishing cadence with dated entries in your sitemap. Consistent publishing signals to AI engines that your site is an active, current information source.',\n },\n schema_coverage: {\n name: 'Deepen Schema Coverage',\n effort: 'Medium',\n description: 'Extend structured data beyond the homepage to inner pages (articles, services, products). Consistent schema coverage across your site helps AI engines understand your full content depth.',\n },\n speakable_schema: {\n name: 'Add Speakable Schema',\n effort: 'Low',\n description: 'Add SpeakableSpecification schema with CSS selectors pointing to key content sections. This tells voice assistants and AI engines which parts of your page are most suitable for spoken answers.',\n },\n query_answer_alignment: {\n name: 'Improve Question-Answer Alignment',\n effort: 'Medium',\n description: 'Ensure every question-format heading (H2/H3) is followed by a direct answer paragraph. This pattern is ideal for AI engine snippet extraction.',\n },\n content_cannibalization: {\n name: 'Resolve Content Cannibalization',\n effort: 'Medium',\n description: 'Multiple pages compete for the same topic. Consolidate overlapping pages or differentiate titles and H1 headings.',\n },\n visible_date_signal: {\n name: 'Add Visible Date Signals',\n effort: 'Low',\n description: 'Display publication/modification dates visibly using <time> elements and add datePublished/dateModified to JSON-LD schema.',\n },\n topic_coherence: {\n name: 'Focus Content on Core Topics',\n effort: 'High',\n description: 'Ensure blog content consistently covers your core expertise areas rather than scattering across unrelated topics. AI engines build authority models - a site about \"Medicare coverage\" that also publishes about humidifiers and groceries dilutes its topical authority.',\n },\n content_depth: {\n name: 'Increase Content Depth',\n effort: 'Medium',\n description: 'Expand articles to 1000+ words with structured H2/H3 sections, comparison tables, and expert analysis. Thin content (under 300 words) is rarely cited by AI engines. Deep, well-structured articles demonstrate expertise.',\n },\n};\n\n// ─── Impact calculation ──────────────────────────────────────────────────────\n\nfunction calculateImpact(score: number, weight: number, effort: string): ImpactLevel {\n const impactScore = (10 - score) * weight * 100;\n\n // Quick win override: low effort + meaningful impact\n if (effort === 'Low' && impactScore >= 3) return 'QUICK WIN';\n\n if (impactScore >= 12) return 'CRITICAL';\n if (impactScore >= 8) return 'HIGH';\n if (impactScore >= 5) return 'CORE AEO';\n if (impactScore >= 3) return 'MEDIUM';\n return 'LOW';\n}\n\n// ─── Verdict generation ──────────────────────────────────────────────────────\n\nexport function generateVerdict(\n score: number,\n scorecard: ScoreCardItem[],\n rawData: RawDataSummary,\n domain: string\n): string {\n // Score-tier opening\n let opening: string;\n if (score >= 86) {\n opening = `Excellent AEO implementation scoring ${score}/100.`;\n } else if (score >= 71) {\n opening = `Strong AEO fundamentals scoring ${score}/100 with room for optimization.`;\n } else if (score >= 56) {\n opening = `Moderate AEO readiness at ${score}/100 with significant gaps to address.`;\n } else if (score >= 41) {\n opening = `Below-average AEO readiness at ${score}/100 - multiple areas need attention.`;\n } else {\n opening = `Critical AEO gaps at ${score}/100 - ${domain} is largely invisible to AI engines.`;\n }\n\n // Top 3 strengths (score >= 8)\n const strengths = scorecard\n .filter(s => s.score >= 8)\n .sort((a, b) => b.score - a.score)\n .slice(0, 3);\n\n // Bottom 3 weaknesses (score <= 4)\n const weaknesses = scorecard\n .filter(s => s.score <= 4)\n .sort((a, b) => a.score - b.score)\n .slice(0, 3);\n\n const parts = [opening];\n\n if (strengths.length > 0) {\n const names = strengths.map(s => s.criterion);\n parts.push(`Key strengths include ${formatList(names)}.`);\n }\n\n if (weaknesses.length > 0) {\n const names = weaknesses.map(s => s.criterion);\n parts.push(`Priority gaps: ${formatList(names)}.`);\n }\n\n // Protocol note\n if (!rawData.has_https) {\n parts.push('HTTPS is not enabled, which caps several criteria scores and reduces AI crawler trust.');\n }\n\n // SPA rendering note\n if (rawData.rendered_with_headless) {\n parts.push('Note: this site uses client-side JavaScript rendering. AI crawlers see an empty page shell instead of content, which is the primary factor limiting the score.');\n }\n\n return parts.join(' ');\n}\n\n// ─── Opportunities generation ────────────────────────────────────────────────\n\nexport function generateOpportunities(\n scorecard: ScoreCardItem[],\n criterionResults: CriterionResult[]\n): Deliverable[] {\n const candidates: Array<{\n criterion: string;\n score: number;\n weight: number;\n impactScore: number;\n template: OpportunityTemplate;\n impact: ImpactLevel;\n }> = [];\n\n for (const result of criterionResults) {\n if (result.score > 7) continue; // Only generate opportunities for scores <= 7\n\n const weight = CRITERION_WEIGHTS[result.criterion] ?? 0.05;\n const template = OPPORTUNITY_TEMPLATES[result.criterion];\n if (!template) continue;\n\n const impactScore = (10 - result.score) * weight * 100;\n const impact = calculateImpact(result.score, weight, template.effort);\n\n candidates.push({\n criterion: result.criterion,\n score: result.score,\n weight,\n impactScore,\n template,\n impact,\n });\n }\n\n // Sort by impact score descending (highest impact first)\n candidates.sort((a, b) => b.impactScore - a.impactScore);\n\n // Take top 8-10\n const top = candidates.slice(0, 10);\n\n return top.map((c, i) => ({\n id: i + 1,\n name: c.template.name,\n description: c.template.description,\n effort: c.template.effort,\n impact: c.impact,\n }));\n}\n\n// ─── Pitch numbers generation ────────────────────────────────────────────────\n\nexport function generatePitchNumbers(\n score: number,\n rawData: RawDataSummary,\n scorecard: ScoreCardItem[]\n): PitchMetric[] {\n const metrics: PitchMetric[] = [];\n\n // 0. Rendering method (SPA warning - prepended so it appears first)\n if (rawData.rendered_with_headless) {\n metrics.push({\n metric: 'Rendering Method',\n value: 'Client-Side Only',\n significance: 'AI crawlers see empty HTML. All content loads via JavaScript, making this site invisible to ChatGPT, Claude, and Perplexity.',\n });\n }\n\n // 1. Overall AEO Score\n metrics.push({\n metric: 'AEO Score',\n value: `${score}/100`,\n significance: score >= 70\n ? 'Above average AI engine visibility'\n : score >= 50\n ? 'Moderate AI visibility with clear improvement paths'\n : 'Below average - significant optimization needed',\n });\n\n // 2. Schema types found\n const schemaCount = rawData.schema_types_found.length;\n metrics.push({\n metric: 'Schema Types',\n value: `${schemaCount} found`,\n significance: schemaCount >= 4\n ? 'Rich structured data helps AI engines parse content'\n : schemaCount >= 1\n ? 'Basic schema present but more types would improve AI extraction'\n : 'No structured data - AI engines cannot reliably extract content',\n });\n\n // 3. AI crawler readiness\n const aiCrawlerCount = rawData.robots_txt_ai_crawlers.length;\n const blockedCount = rawData.robots_txt_blocked_crawlers.length;\n metrics.push({\n metric: 'AI Crawler Access',\n value: blockedCount > 0\n ? `${blockedCount} blocked`\n : aiCrawlerCount > 0\n ? `${aiCrawlerCount} configured`\n : 'Not configured',\n significance: blockedCount > 0\n ? 'Active AI crawlers are blocked from accessing content'\n : aiCrawlerCount > 0\n ? 'robots.txt explicitly addresses AI crawler access'\n : 'No AI-specific crawler directives in robots.txt',\n });\n\n // 4. Content pages indexed\n const sitemapUrls = rawData.sitemap_url_count;\n metrics.push({\n metric: 'Sitemap URLs',\n value: sitemapUrls > 0 ? `${sitemapUrls} pages` : 'No sitemap',\n significance: sitemapUrls >= 50\n ? 'Comprehensive content library discoverable by AI crawlers'\n : sitemapUrls >= 10\n ? 'Moderate content footprint in sitemap'\n : sitemapUrls > 0\n ? 'Small sitemap - expanding content improves AI coverage'\n : 'No sitemap means AI crawlers must discover pages via links only',\n });\n\n // 5. Internal linking\n const linkCount = rawData.internal_link_count;\n metrics.push({\n metric: 'Internal Links',\n value: `${linkCount} links`,\n significance: linkCount >= 30\n ? 'Strong internal linking supports AI content discovery'\n : linkCount >= 10\n ? 'Moderate linking - adding more cross-references improves navigability'\n : 'Weak internal linking limits AI crawler depth',\n });\n\n // 6. Question headings\n const questionCount = rawData.question_headings_count + (rawData.blog_sample_question_headings || 0);\n if (questionCount > 0) {\n metrics.push({\n metric: 'Question Headings',\n value: `${questionCount} found`,\n significance: 'Question-based headings match how users query AI assistants',\n });\n }\n\n // 7. Criteria passing (>= 7)\n const passing = scorecard.filter(s => s.score >= 7).length;\n metrics.push({\n metric: 'Criteria Passing',\n value: `${passing}/28`,\n significance: passing >= 18\n ? 'Excellent coverage across AEO dimensions'\n : passing >= 12\n ? 'Good foundation with room to improve remaining criteria'\n : `${26 - passing} criteria need attention for full AI visibility`,\n });\n\n return metrics;\n}\n\n// ─── Bottom line generation ──────────────────────────────────────────────────\n\nexport function generateBottomLine(\n score: number,\n opportunities: Deliverable[],\n scorecard: ScoreCardItem[],\n domain: string\n): string {\n const quickWins = opportunities.filter(o => o.impact === 'QUICK WIN');\n const criticalOps = opportunities.filter(o => o.impact === 'CRITICAL' || o.impact === 'HIGH');\n\n const passing = scorecard.filter(s => s.score >= 7).length;\n const total = scorecard.length;\n\n let summary: string;\n if (score >= 86) {\n summary = `${domain} demonstrates excellent AI engine optimization with ${passing}/${total} criteria at good or strong levels. Focus on maintaining content freshness and expanding structured data coverage to stay ahead.`;\n } else if (score >= 71) {\n summary = `${domain} has a solid AEO foundation with ${passing}/${total} criteria passing.`;\n if (quickWins.length > 0) {\n summary += ` ${quickWins.length} quick wins available: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n if (criticalOps.length > 0) {\n summary += ` Address ${criticalOps.length} high-impact opportunities to push the score above 85.`;\n }\n } else if (score >= 56) {\n summary = `${domain} has moderate AI visibility with ${passing}/${total} criteria passing. ${opportunities.length} improvement opportunities identified.`;\n if (quickWins.length > 0) {\n summary += ` Start with quick wins: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n } else if (score >= 41) {\n summary = `${domain} needs significant AEO work with only ${passing}/${total} criteria passing.`;\n if (criticalOps.length > 0) {\n summary += ` Priority: ${criticalOps.slice(0, 3).map(c => c.name).join(', ')}.`;\n }\n summary += ` Implementing the top ${Math.min(5, opportunities.length)} recommendations could improve the score by 15-25 points.`;\n } else {\n summary = `${domain} is largely invisible to AI engines with only ${passing}/${total} criteria passing. Fundamental AEO infrastructure is missing.`;\n if (opportunities.length > 0) {\n summary += ` Start with: ${opportunities.slice(0, 3).map(o => o.name).join(', ')}.`;\n }\n summary += ` A comprehensive AEO implementation could transform AI visibility from near-zero to competitive.`;\n }\n\n return summary;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return '';\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;\n}\n","/**\n * Extended page discovery for instant audit.\n * Fetches additional pages beyond what prefetchSiteData provides,\n * including nav-linked pages, common paths, and content pages from sitemap.\n */\n\nimport type { FetchResult, SiteData, PageCategory } from './site-crawler.js';\n\n// ─── Fetch helper (matches site-crawler.ts fetchText) ────────────────────────\n\nasync function fetchPage(url: string, timeoutMs = 10000): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(timeoutMs),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n if (res.status !== 200) return null;\n const text = await res.text();\n if (text.length < 200) return null; // Skip trivially small pages\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n// ─── Page variant paths ──────────────────────────────────────────────────────\n\nconst PAGE_VARIANTS: Record<string, string[]> = {\n about: ['/about', '/about-us', '/company', '/who-we-are'],\n pricing: ['/pricing', '/plans', '/packages'],\n services: ['/services', '/features', '/solutions', '/products', '/what-we-do'],\n contact: ['/contact', '/contact-us', '/get-in-touch'],\n team: ['/team', '/our-team', '/authors', '/people', '/leadership'],\n resources: ['/resources', '/resource-center', '/library'],\n docs: ['/docs', '/documentation', '/help', '/help-center', '/support'],\n cases: ['/case-studies', '/customers', '/success-stories', '/testimonials'],\n};\n\n// ─── Nav link extraction ─────────────────────────────────────────────────────\n\n/**\n * Extract internal page paths from <nav> elements in homepage HTML.\n * Returns deduplicated absolute paths (e.g. ['/about', '/pricing']).\n */\nexport function extractNavLinks(html: string, domain: string): string[] {\n // Extract links from <nav> elements\n const navBlocks = html.match(/<nav[\\s\\S]*?<\\/nav>/gi) || [];\n const navHtml = navBlocks.join('\\n');\n\n const hrefMatches = navHtml.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize: strip trailing slash, skip root and fragments\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#')) continue;\n\n // Skip resource/utility paths\n if (/\\.(js|css|png|jpg|svg|ico|pdf|xml|txt)$/i.test(path)) continue;\n if (/^\\/(api|wp-|static|assets|_next|auth|login|signup|cart|checkout)\\b/i.test(path)) continue;\n\n paths.add(path);\n }\n\n return Array.from(paths);\n}\n\n// ─── Full-page link extraction (fallback when no sitemap) ─────────────────\n\n/**\n * Extract ALL internal links from the full page HTML (not just <nav>).\n * Used as a fallback when no sitemap exists to build our own page list.\n * Returns deduplicated absolute paths, filtered and capped.\n */\nexport function extractAllInternalLinks(html: string, domain: string, limit = 30): string[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const hrefMatches = html.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#') || path.includes('?')) continue;\n\n // Skip resource files and utility paths\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|zip|woff|woff2|ttf|eot|mp4|webm|mp3)$/i.test(path)) continue;\n if (/^\\/(api|wp-admin|wp-includes|wp-json|static|assets|_next|auth|login|signup|sign-up|register|cart|checkout|account|admin|cdn-cgi|feed|rss)\\b/i.test(path)) continue;\n\n // Skip anchors-only and mailto/tel\n if (path.startsWith('mailto:') || path.startsWith('tel:')) continue;\n\n paths.add(path);\n }\n\n // Return up to limit paths, prioritizing shorter paths (more likely to be main pages)\n return Array.from(paths)\n .sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b))\n .slice(0, limit);\n}\n\n// ─── Content page extraction from sitemap ────────────────────────────────────\n\n/**\n * Extract non-blog deep content pages from sitemap XML.\n * Targets service pages, product pages, etc. (not blog/article posts).\n */\nexport function extractContentPagesFromSitemap(\n sitemapText: string,\n domain: string,\n limit = 6\n): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const candidates: string[] = [];\n\n // Paths to skip (already covered by blog sample or common pages)\n const skipPatterns = /\\/(?:blog|articles?|posts?|news|tag|category|author|feed|faq|about|pricing|contact|team|resources?|docs?|documentation|help|support|case-studies|customers|testimonials|sitemap|wp-|api|login|cart|checkout|search)\\b/i;\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n if (skipPatterns.test(path)) continue;\n\n // Want pages with 1-2 path segments (service pages, product pages)\n const segments = path.split('/').filter(Boolean);\n if (segments.length < 1 || segments.length > 3) continue;\n\n candidates.push(url);\n } catch {\n continue;\n }\n }\n\n // Return evenly spaced pages from the list for variety\n if (candidates.length <= limit) return candidates;\n\n const result: string[] = [];\n for (let i = 0; i < limit; i++) {\n const index = Math.round(i * (candidates.length - 1) / (limit - 1));\n result.push(candidates[index]);\n }\n return result;\n}\n\n// ─── Main multi-page fetcher ─────────────────────────────────────────────────\n\nexport interface MultiPageOptions {\n timeoutMs?: number;\n}\n\n/**\n * Fetch additional pages beyond what prefetchSiteData provides.\n * Discovers pages from nav links + common path variants + sitemap content pages.\n * All fetched pages are appended to siteData.blogSample so existing\n * getCombinedHtml() and criteria checks pick them up automatically.\n *\n * Mutates siteData in place and returns the count of new pages added.\n */\nexport async function fetchMultiPageData(\n siteData: SiteData,\n options?: MultiPageOptions\n): Promise<number> {\n if (!siteData.protocol || !siteData.homepage) return 0;\n\n const timeoutMs = options?.timeoutMs ?? 10000;\n const baseUrl = `${siteData.protocol}://${siteData.domain}`;\n const existingUrls = new Set<string>();\n\n // Track already-fetched URLs\n existingUrls.add(baseUrl + '/');\n existingUrls.add(baseUrl);\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n if (page.finalUrl) existingUrls.add(page.finalUrl);\n }\n }\n\n // Collect candidate URLs to fetch (URL -> category)\n const urlsToFetch = new Map<string, PageCategory>();\n\n // Source 1: Nav links from homepage\n const navPaths = extractNavLinks(siteData.homepage.text, siteData.domain);\n\n // Source 2: Common page variants (try nav links first, then fallback paths)\n for (const [category, variants] of Object.entries(PAGE_VARIANTS)) {\n // Check if any nav link matches this category\n const navMatch = navPaths.find(p =>\n variants.some(v => p.toLowerCase() === v || p.toLowerCase().startsWith(v + '/'))\n );\n\n if (navMatch) {\n const url = `${baseUrl}${navMatch}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n } else {\n // Try first variant as fallback\n const url = `${baseUrl}${variants[0]}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n }\n }\n\n // Source 3: Content pages from sitemap\n if (siteData.sitemapXml && siteData.sitemapXml.status === 200) {\n const contentUrls = extractContentPagesFromSitemap(\n siteData.sitemapXml.text,\n siteData.domain,\n 6\n );\n for (const url of contentUrls) {\n if (!existingUrls.has(url)) urlsToFetch.set(url, 'content');\n }\n }\n\n // Source 4: When no sitemap blog sample exists, extract ALL internal links\n // from homepage as a fallback to build our own page list\n const hasBlogSample = (siteData.blogSample?.length ?? 0) > 3;\n if (!hasBlogSample) {\n const allPaths = extractAllInternalLinks(siteData.homepage.text, siteData.domain, 30);\n for (const path of allPaths) {\n const url = `${baseUrl}${path}`;\n if (!existingUrls.has(url) && !urlsToFetch.has(url)) {\n urlsToFetch.set(url, 'content');\n }\n }\n }\n\n // Fetch all URLs in parallel\n const entries = Array.from(urlsToFetch.entries());\n if (entries.length === 0) return 0;\n\n const results = await Promise.all(entries.map(([url]) => fetchPage(url, timeoutMs)));\n\n // Append successful results to blogSample with category tags\n if (!siteData.blogSample) siteData.blogSample = [];\n\n let added = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result && result.text.length > 500) {\n result.category = entries[i][1];\n siteData.blogSample.push(result);\n added++;\n }\n }\n\n return added;\n}\n","/**\n * Per-page AEO scoring.\n * Evaluates 14 of 28 criteria that apply at individual page level.\n * Produces a 0-100 AEO score per page.\n */\n\nimport type { SiteData } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PageCriterionScore {\n criterion: string;\n criterion_label: string;\n score: number;\n weight: number;\n}\n\nexport interface PageScoreResult {\n aeoScore: number;\n criterionScores: PageCriterionScore[];\n}\n\n// ─── Criteria weights (mirrored from scoring.ts for the 14 page-level criteria) ─\n\nconst PAGE_CRITERIA: Record<string, { weight: number; label: string }> = {\n // Content Substance\n original_data: { weight: 0.10, label: 'Original Data & Expert Content' },\n fact_density: { weight: 0.06, label: 'Fact & Data Density' },\n direct_answer_density: { weight: 0.05, label: 'Direct Answer Paragraphs' },\n qa_content_format: { weight: 0.05, label: 'Q&A Content Format' },\n query_answer_alignment: { weight: 0.05, label: 'Query-Answer Alignment' },\n faq_section: { weight: 0.04, label: 'FAQ Section Content' },\n // Content Organization\n content_freshness: { weight: 0.04, label: 'Content Freshness Signals' },\n schema_markup: { weight: 0.03, label: 'Schema.org Structured Data' },\n table_list_extractability: { weight: 0.03, label: 'Table & List Extractability' },\n definition_patterns: { weight: 0.02, label: 'Definition Patterns' },\n visible_date_signal: { weight: 0.02, label: 'Visible Date Signal' },\n semantic_html: { weight: 0.02, label: 'Semantic HTML5 & Accessibility' },\n clean_html: { weight: 0.02, label: 'Clean, Crawlable HTML' },\n // Technical Plumbing\n canonical_url: { weight: 0.01, label: 'Canonical URL Strategy' },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractJsonLdBlocks(html: string): string[] {\n const blocks: string[] = [];\n const regex = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(html)) !== null) {\n blocks.push(match[1]);\n }\n return blocks;\n}\n\nfunction extractTypesFromJsonLd(blocks: string[]): Set<string> {\n const types = new Set<string>();\n for (const block of blocks) {\n const typeMatches = block.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n return types;\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction extractQuestionHeadings(html: string): string[] {\n const headings = html.match(/<h[2-3][^>]*>([\\s\\S]*?)<\\/h[2-3]>/gi) || [];\n const questions: string[] = [];\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questions.push(text);\n }\n }\n return questions;\n}\n\n/** Check if a question heading is followed by an answer paragraph (≥20 chars). */\nfunction countAnsweredQuestions(html: string): { total: number; answered: number } {\n const questions = extractQuestionHeadings(html);\n if (questions.length === 0) return { total: 0, answered: 0 };\n\n let answered = 0;\n for (const q of questions) {\n // Escape the question text for regex matching\n const escaped = q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // Look for the heading containing this text, then a <p> after it\n const pattern = new RegExp(escaped + '[\\\\s\\\\S]*?</h[2-3]>\\\\s*<p[^>]*>([\\\\s\\\\S]*?)</p>', 'i');\n const match = html.match(pattern);\n if (match && match[1].replace(/<[^>]*>/g, '').trim().length >= 20) {\n answered++;\n }\n }\n return { total: questions.length, answered };\n}\n\nfunction cap(value: number, max: number): number {\n return Math.min(value, max);\n}\n\n// ─── 14 Scoring Functions ───────────────────────────────────────────────────\n\n/** 1. Schema.org Structured Data */\nexport function scoreSchemaMarkup(html: string): number {\n const blocks = extractJsonLdBlocks(html);\n if (blocks.length === 0) return 0;\n\n let score = 3; // has JSON-LD\n const types = extractTypesFromJsonLd(blocks);\n\n const knownTypes = ['Organization', 'LocalBusiness', 'Article', 'FAQPage', 'Product', 'WebPage',\n 'BreadcrumbList', 'HowTo', 'Person', 'WebSite', 'BlogPosting', 'Service'];\n let knownCount = 0;\n for (const t of types) {\n if (knownTypes.includes(t)) knownCount++;\n }\n score += cap(knownCount * 2, 4);\n\n if (types.has('Organization') || types.has('LocalBusiness')) score += 2;\n if (types.has('FAQPage')) score += 1;\n\n return cap(score, 10);\n}\n\n/** 2. Q&A Content Format */\nexport function scoreQAFormat(html: string): number {\n const questions = extractQuestionHeadings(html);\n let score = 0;\n\n if (questions.length >= 10) score += 5;\n else if (questions.length >= 3) score += 3;\n else if (questions.length >= 1) score += 1;\n\n // Direct-answer format: question heading followed by paragraph ≥20 chars\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 1) score += 3;\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n return cap(score, 10);\n}\n\n/** 3. Clean, Crawlable HTML */\nexport function scoreCleanHtml(html: string): number {\n let score = 0;\n\n // Semantic elements\n const semantics = ['<main', '<article', '<section'];\n let semCount = 0;\n for (const tag of semantics) {\n if (html.toLowerCase().includes(tag)) semCount++;\n }\n score += cap(semCount, 3);\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n // Text content > 500 chars\n const text = getTextContent(html);\n if (text.length > 500) score += 3;\n\n // Title + meta description\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (hasTitle && hasDesc) score += 2;\n\n return cap(score, 10);\n}\n\n/** 4. FAQ Section Content */\nexport function scoreFaqSection(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // FAQ/frequently-asked text\n if (/frequently\\s*asked|faq/i.test(html)) score += 2;\n\n // FAQPage in JSON-LD\n const blocks = extractJsonLdBlocks(html);\n const types = extractTypesFromJsonLd(blocks);\n if (types.has('FAQPage')) score += 3;\n\n // Question headings ≥ 10\n const questions = extractQuestionHeadings(html);\n if (questions.length >= 10) score += 1;\n\n // Accordion/toggle patterns (details/summary or common CSS classes)\n if (/<details[\\s>]/i.test(html) || /accordion|collapsible|toggle/i.test(lowerHtml)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 5. Original Data & Expert Content */\nexport function scoreOriginalData(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Stats with research context\n if (/\\b(our (study|analysis|research|survey|data|findings))\\b/i.test(text)) {\n score += 3;\n } else if (/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(text)) {\n score += 1;\n }\n\n // Case studies with metrics\n if (/\\bcase\\s+stud(y|ies)\\b/i.test(text) && /\\d+(\\.\\d+)?%|\\$[\\d,.]+/.test(text)) {\n score += 3;\n } else if (/\\bcase\\s+stud(y|ies)\\b/i.test(text)) {\n score += 1;\n }\n\n // Expert attribution\n if (/\\baccording\\s+to\\b|\\bexpert|\\b(Ph\\.?D|MD|professor|analyst|researcher)\\b/i.test(text)) {\n score += 2;\n }\n\n // Blog content (internal links to /blog)\n if (/href=[\"'][^\"']*\\/blog\\b/i.test(html)) {\n score += 2;\n }\n\n return cap(score, 10);\n}\n\n/** 6. Query-Answer Alignment */\nexport function scoreQueryAnswerAlignment(html: string): number {\n const { total, answered } = countAnsweredQuestions(html);\n\n // No question headings = neutral\n if (total === 0) return 5;\n\n const ratio = answered / total;\n if (ratio >= 0.8) return 10;\n if (ratio >= 0.5) return 7;\n if (answered > 0) return 4;\n return 0;\n}\n\n/** 7. Content Freshness Signals */\nexport function scoreContentFreshness(html: string): number {\n let score = 0;\n const blocks = extractJsonLdBlocks(html);\n\n // JSON-LD datePublished/dateModified\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) score += 3;\n else if (timeElements.length === 1) score += 1;\n\n // Article meta tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // Current or previous year reference\n const currentYear = new Date().getFullYear();\n const yearPattern = new RegExp(`\\\\b(${currentYear}|${currentYear - 1})\\\\b`);\n if (yearPattern.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 8. Table & List Extractability */\nexport function scoreTableListExtractability(html: string): number {\n let score = 0;\n\n // Tables with <th>\n const tablesWithHeaders = html.match(/<table[\\s\\S]*?<th[\\s>]/gi) || [];\n if (tablesWithHeaders.length >= 2) score += 4;\n else if (tablesWithHeaders.length === 1) score += 3;\n\n // Tables without headers\n if (tablesWithHeaders.length === 0 && /<table[\\s>]/i.test(html)) score += 1;\n\n // Ordered lists\n if (/<ol[\\s>]/i.test(html)) score += 2;\n\n // Unordered lists\n if (/<ul[\\s>]/i.test(html)) score += 2;\n\n // ≥10 list items\n const listItems = html.match(/<li[\\s>]/gi) || [];\n if (listItems.length >= 10) score += 1;\n\n // Definition lists\n if (/<dl[\\s>]/i.test(html)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 9. Direct Answer Paragraphs */\nexport function scoreDirectAnswerDensity(html: string): number {\n let score = 0;\n\n // Q&A pairs (question heading + <p>)\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 3) score += 6;\n else if (answered >= 1) score += 3;\n\n // Snippet-zone paragraphs (40-150 words)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n let snippetCount = 0;\n for (const p of paragraphs) {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const words = text.split(/\\s+/).filter(w => w.length > 0).length;\n if (words >= 40 && words <= 150) snippetCount++;\n }\n if (snippetCount >= 3) score += 2;\n else if (snippetCount >= 1) score += 1;\n\n // Direct-answer openers\n const directOpeners = getTextContent(html).match(/\\b(yes|no|in short|the answer is|simply put|in summary)\\b/gi) || [];\n if (directOpeners.length >= 2) score += 2;\n\n return cap(score, 10);\n}\n\n/** 10. Semantic HTML5 & Accessibility */\nexport function scoreSemanticHtml(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // 6 semantic elements\n const elements = ['<main', '<article', '<time', '<nav', '<header', '<footer'];\n let count = 0;\n for (const el of elements) {\n if (lowerHtml.includes(el)) count++;\n }\n score += cap(Math.floor(count * 0.7), 4);\n\n // Image alt ratio ≥ 80%\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length > 0) {\n let withAlt = 0;\n for (const img of imgTags) {\n if (/\\salt=[\"'][^\"']*[\"']/i.test(img)) withAlt++;\n }\n if (withAlt / imgTags.length >= 0.8) score += 2;\n }\n\n // lang attribute\n if (/<html[^>]*\\slang=[\"'][^\"']+[\"']/i.test(html)) score += 2;\n\n // ARIA attributes\n if (/\\baria-/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 11. Fact & Data Density */\nexport function scoreFactDensity(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Numeric data points (%, $, count phrases)\n const numericPatterns = text.match(/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b|\\b\\d+\\s*(million|billion|thousand|users|customers|employees)\\b/gi) || [];\n if (numericPatterns.length >= 6) score += 5;\n else if (numericPatterns.length >= 3) score += 3;\n else if (numericPatterns.length >= 1) score += 1;\n\n // Year references\n const years = new Set<string>();\n const yearMatches = text.match(/\\b(19|20)\\d{2}\\b/g) || [];\n for (const y of yearMatches) years.add(y);\n if (years.size >= 2) score += 2;\n else if (years.size === 1) score += 1;\n\n // Attribution phrases\n if (/\\baccording to\\b|\\bsource:\\s|\\bcited\\b|\\breported by\\b/i.test(text)) score += 2;\n\n // Units (kg, miles, hours, etc.)\n const units = text.match(/\\b\\d+\\s*(kg|lb|miles|km|hours|minutes|days|months|years|GB|MB|TB)\\b/gi) || [];\n if (units.length >= 2) score += 1;\n\n return cap(score, 10);\n}\n\n/** 12. Definition Patterns */\nexport function scoreDefinitionPatterns(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition phrases\n const defPatterns = text.match(/\\b(is a|is an|refers to|defined as|means that|also known as|abbreviated as)\\b/gi) || [];\n if (defPatterns.length >= 3) score += 5;\n else if (defPatterns.length >= 1) score += 3;\n\n // Early placement (first 2000 chars)\n const early = text.slice(0, 2000);\n if (/\\b(is a|is an|refers to|defined as)\\b/i.test(early)) score += 2;\n\n // <dfn> or <abbr>\n if (/<dfn[\\s>]/i.test(html) || /<abbr[\\s>]/i.test(html)) score += 1;\n\n // <dl> or glossary\n if (/<dl[\\s>]/i.test(html) || /glossary/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 13. Canonical URL Strategy */\nexport function scoreCanonicalUrl(html: string, url?: string): number {\n let score = 0;\n\n // Has canonical link\n const canonicalMatch = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i)\n || html.match(/<link[^>]*href=[\"']([^\"']+)[\"'][^>]*rel=[\"']canonical[\"']/i);\n\n if (!canonicalMatch) return 0;\n score += 4;\n\n const canonicalHref = canonicalMatch[1];\n\n // Self-referencing\n if (url) {\n try {\n const canonicalUrl = new URL(canonicalHref, url);\n const pageUrl = new URL(url);\n if (canonicalUrl.pathname === pageUrl.pathname && canonicalUrl.hostname === pageUrl.hostname) {\n score += 3;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n\n // HTTPS\n if (canonicalHref.startsWith('https://')) score += 2;\n\n // Single canonical (no duplicates)\n const allCanonicals = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*>/gi) || [];\n if (allCanonicals.length === 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 14. Visible Date Signal */\nexport function scoreVisibleDateSignal(html: string): number {\n let score = 0;\n\n // <time datetime> with visible text\n const timeWithDatetime = html.match(/<time[^>]*datetime=[\"'][^\"']+[\"'][^>]*>[^<]+<\\/time>/gi) || [];\n if (timeWithDatetime.length > 0) score += 5;\n\n // JSON-LD datePublished/dateModified\n const blocks = extractJsonLdBlocks(html);\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // Article meta date tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // dateModified within 180 days\n const modifiedMatch = allJsonLd.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (modifiedMatch) {\n try {\n const modified = new Date(modifiedMatch[1]);\n const daysDiff = (Date.now() - modified.getTime()) / (1000 * 60 * 60 * 24);\n if (daysDiff <= 180) score += 1;\n } catch {\n // Ignore date parse errors\n }\n }\n\n return cap(score, 10);\n}\n\n// ─── Scoring function map ───────────────────────────────────────────────────\n\nconst SCORING_FUNCTIONS: Record<string, (html: string, url?: string) => number> = {\n schema_markup: scoreSchemaMarkup,\n qa_content_format: scoreQAFormat,\n clean_html: scoreCleanHtml,\n faq_section: scoreFaqSection,\n original_data: scoreOriginalData,\n query_answer_alignment: scoreQueryAnswerAlignment,\n content_freshness: scoreContentFreshness,\n table_list_extractability: scoreTableListExtractability,\n direct_answer_density: scoreDirectAnswerDensity,\n semantic_html: scoreSemanticHtml,\n fact_density: scoreFactDensity,\n definition_patterns: scoreDefinitionPatterns,\n canonical_url: scoreCanonicalUrl,\n visible_date_signal: scoreVisibleDateSignal,\n};\n\n// ─── Main API ───────────────────────────────────────────────────────────────\n\n/**\n * Score a single page against 14 AEO criteria.\n * Returns a 0-100 AEO score and individual criterion scores.\n */\nexport function scorePage(html: string, url?: string): PageScoreResult {\n let totalWeight = 0;\n let weightedSum = 0;\n const criterionScores: PageCriterionScore[] = [];\n\n for (const [criterion, { weight, label }] of Object.entries(PAGE_CRITERIA)) {\n const fn = SCORING_FUNCTIONS[criterion]!;\n const score = fn(html, url);\n\n criterionScores.push({ criterion, criterion_label: label, score, weight });\n weightedSum += (score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n const aeoScore = totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n\n return { aeoScore, criterionScores };\n}\n\n/**\n * Score all crawled pages (homepage + blogSample).\n */\nexport function scoreAllPages(siteData: SiteData): PageScoreResult[] {\n const results: PageScoreResult[] = [];\n\n if (siteData.homepage) {\n const url = siteData.protocol ? `${siteData.protocol}://${siteData.domain}` : undefined;\n results.push(scorePage(siteData.homepage.text, url));\n }\n\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || undefined;\n results.push(scorePage(page.text, url));\n }\n }\n\n return results;\n}\n","/**\n * Per-page analysis for instant audit.\n * Runs 12 deterministic checks on each crawled page (no LLM).\n */\n\nimport type { PageCategory, SiteData } from './site-crawler.js';\nimport type { PageIssue, PageReview } from './types.js';\nimport { scorePage } from './page-scorer.js';\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Individual checks ──────────────────────────────────────────────────────\n\nfunction checkMissingTitle(html: string): PageIssue | null {\n const hasTitle = /<title[^>]*>[\\s\\S]*?<\\/title>/i.test(html);\n if (!hasTitle) {\n return { check: 'missing-title', label: 'Missing <title> tag', severity: 'error' };\n }\n const title = extractTitle(html);\n if (!title) {\n return { check: 'missing-title', label: 'Empty <title> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMissingMetaDescription(html: string): PageIssue | null {\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (!hasDesc) {\n return { check: 'missing-meta-description', label: 'Missing meta description', severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (!h1Matches || h1Matches.length === 0) {\n return { check: 'no-h1', label: 'No <h1> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMultipleH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (h1Matches && h1Matches.length > 1) {\n return { check: 'multiple-h1', label: `Multiple <h1> tags (${h1Matches.length})`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkNoSchema(html: string): PageIssue | null {\n const hasLdJson = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>/i.test(html);\n if (!hasLdJson) {\n return { check: 'no-schema', label: 'No JSON-LD structured data', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingCanonical(html: string): PageIssue | null {\n const hasCanonical = /<link[^>]*rel=[\"']canonical[\"'][^>]*>/i.test(html);\n if (!hasCanonical) {\n return { check: 'missing-canonical', label: 'Missing canonical link', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingOgTags(html: string): PageIssue | null {\n const hasOg = /<meta\\s[^>]*property=[\"']og:/i.test(html);\n if (!hasOg) {\n return { check: 'missing-og-tags', label: 'No Open Graph tags', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkThinContent(wordCount: number): PageIssue | null {\n if (wordCount < 300) {\n return { check: 'thin-content', label: `Thin content (${wordCount} words)`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkImagesMissingAlt(html: string): PageIssue | null {\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length === 0) return null;\n\n let missingAlt = 0;\n for (const img of imgTags) {\n const hasAlt = /\\salt=[\"'][^\"']+[\"']/i.test(img);\n const hasEmptyAlt = /\\salt=[\"'][\"']/i.test(img); // decorative\n if (!hasAlt && !hasEmptyAlt) missingAlt++;\n }\n\n if (missingAlt > 0) {\n return {\n check: 'images-missing-alt',\n label: `${missingAlt} image${missingAlt > 1 ? 's' : ''} missing alt text`,\n severity: 'warning',\n };\n }\n return null;\n}\n\nfunction checkNoInternalLinks(html: string, url: string): PageIssue | null {\n let domain: string;\n try {\n domain = new URL(url).hostname.replace(/^www\\./, '').toLowerCase();\n } catch {\n return null;\n }\n\n const links = html.match(/<a\\s[^>]*href=[\"']([^\"'#]*?)[\"'][^>]*>/gi) || [];\n let internalCount = 0;\n\n for (const link of links) {\n const hrefMatch = link.match(/href=[\"']([^\"'#]*?)[\"']/i);\n if (!hrefMatch) continue;\n const href = hrefMatch[1];\n\n if (href.startsWith('/') && !href.startsWith('//')) {\n internalCount++;\n } else if (href.startsWith('http')) {\n try {\n const linkDomain = new URL(href).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain === domain) internalCount++;\n } catch {\n // skip\n }\n }\n }\n\n if (internalCount === 0) {\n return { check: 'no-internal-links', label: 'No internal links found', severity: 'warning' };\n }\n return null;\n}\n\n// ─── Strength checks ────────────────────────────────────────────────────────\n\nfunction checkHasStructuredData(html: string): PageIssue | null {\n const ldBlocks = html.match(/<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (ldBlocks.length === 0) return null;\n\n const types = new Set<string>();\n for (const block of ldBlocks) {\n const content = block.replace(/<\\/?script[^>]*>/gi, '');\n const typeMatches = content.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n\n if (types.size > 0) {\n return {\n check: 'has-structured-data',\n label: `JSON-LD: ${Array.from(types).join(', ')}`,\n severity: 'info',\n };\n }\n return null;\n}\n\nfunction checkHasQuestionHeadings(html: string): PageIssue | null {\n const headings = html.match(/<h[2-4][^>]*>[\\s\\S]*?<\\/h[2-4]>/gi) || [];\n let questionCount = 0;\n\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questionCount++;\n }\n }\n\n if (questionCount > 0) {\n return {\n check: 'has-question-headings',\n label: `${questionCount} question-format heading${questionCount > 1 ? 's' : ''}`,\n severity: 'info',\n };\n }\n return null;\n}\n\n// ─── Main analyzers ─────────────────────────────────────────────────────────\n\nexport function analyzePage(html: string, url: string, category: PageCategory): PageReview {\n const title = extractTitle(html);\n const textContent = getTextContent(html);\n const wordCount = countWords(textContent);\n\n const issues: PageIssue[] = [];\n const strengths: PageIssue[] = [];\n\n // Issue checks\n const issueChecks = [\n checkMissingTitle(html),\n checkMissingMetaDescription(html),\n checkNoH1(html),\n checkMultipleH1(html),\n checkNoSchema(html),\n checkMissingCanonical(html),\n checkMissingOgTags(html),\n checkThinContent(wordCount),\n checkImagesMissingAlt(html),\n checkNoInternalLinks(html, url),\n ];\n\n for (const result of issueChecks) {\n if (result) issues.push(result);\n }\n\n // Strength checks\n const strengthChecks = [\n checkHasStructuredData(html),\n checkHasQuestionHeadings(html),\n ];\n\n for (const result of strengthChecks) {\n if (result) strengths.push(result);\n }\n\n const { aeoScore, criterionScores } = scorePage(html, url);\n return { url, title, category, wordCount, issues, strengths, aeoScore, criterionScores };\n}\n\nexport function analyzeAllPages(siteData: SiteData): PageReview[] {\n const reviews: PageReview[] = [];\n\n // Analyze homepage\n if (siteData.homepage) {\n const url = `${siteData.protocol}://${siteData.domain}`;\n reviews.push(analyzePage(siteData.homepage.text, url, siteData.homepage.category || 'homepage'));\n }\n\n // Analyze all pages in blogSample\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || 'unknown';\n reviews.push(analyzePage(page.text, url, page.category || 'content'));\n }\n }\n\n return reviews;\n}\n","/**\n * Programmatic audit API.\n * Runs the full 7-phase AEO audit pipeline and returns structured results.\n */\n\nimport { prefetchSiteData, auditSiteFromData, extractRawDataSummary } from './site-crawler.js';\nimport { calculateOverallScore } from './scoring.js';\nimport { isSpaShell, fetchWithHeadless, classifyRendering } from './headless-fetch.js';\nimport { buildScorecard, buildDetailedFindings } from './scorecard-builder.js';\nimport { generateVerdict, generateOpportunities, generatePitchNumbers, generateBottomLine } from './narrative-generator.js';\nimport { fetchMultiPageData } from './multi-page-fetcher.js';\nimport { analyzeAllPages } from './page-analyzer.js';\nimport type { AuditData } from './types.js';\n\nexport interface AuditOptions {\n /** Skip Puppeteer SPA rendering (default: false) */\n noHeadless?: boolean;\n /** Homepage + blog only, skip extra page discovery (default: false) */\n noMultiPage?: boolean;\n /** Fetch timeout in ms (default: 15000) */\n timeout?: number;\n /** Enable full-site BFS crawl (default: false) */\n fullCrawl?: boolean;\n /** Max pages for full crawl (default: 200) */\n maxPages?: number;\n /** Parallel fetch concurrency for full crawl (default: 5) */\n concurrency?: number;\n}\n\nexport interface AuditResult extends AuditData {\n /** True if headless browser was used for SPA rendering */\n renderedWithHeadless?: boolean;\n /** Wall-clock seconds */\n elapsed: number;\n}\n\nfunction getTextLength(html: string): number {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim().length;\n}\n\n/**\n * Run a complete AEO audit on a domain.\n *\n * @example\n * ```ts\n * import { audit } from 'aeorank';\n * const result = await audit('example.com');\n * console.log(result.overallScore); // 0-100\n * ```\n */\nexport async function audit(domain: string, options?: AuditOptions): Promise<AuditResult> {\n const startTime = Date.now();\n let renderedWithHeadless = false;\n\n // Phase 1: Fetch site data\n const siteData = await prefetchSiteData(domain);\n\n if (!siteData.protocol) {\n throw new Error(`Could not connect to ${domain} (no HTTPS or HTTP response)`);\n }\n\n if (siteData.redirectedTo) {\n throw new Error(`${domain} redirects to ${siteData.redirectedTo} (hijacked domain)`);\n }\n\n if (siteData.parkedReason) {\n throw new Error(`${domain} is a parked/lost domain (${siteData.parkedReason})`);\n }\n\n // Phase 2: SPA detection + headless rendering\n if (!options?.noHeadless && siteData.homepage && isSpaShell(siteData.homepage.text)) {\n const rawTextLen = getTextLength(siteData.homepage.text);\n const url = `${siteData.protocol}://${domain}`;\n const rendered = await fetchWithHeadless(url);\n\n if (rendered) {\n const renderedTextLen = getTextLength(rendered.text);\n if (renderedTextLen > rawTextLen) {\n siteData.homepage = rendered;\n renderedWithHeadless = true;\n }\n }\n\n if (renderedWithHeadless && siteData.faqPage && isSpaShell(siteData.faqPage.text)) {\n const faqUrl = `${siteData.protocol}://${domain}/faq`;\n const renderedFaq = await fetchWithHeadless(faqUrl);\n if (renderedFaq && getTextLength(renderedFaq.text) > getTextLength(siteData.faqPage.text)) {\n siteData.faqPage = renderedFaq;\n }\n }\n }\n\n // Phase 3: Multi-page discovery\n if (options?.fullCrawl) {\n const { crawlFullSite } = await import('./full-site-crawler.js');\n const crawlResult = await crawlFullSite(siteData, {\n maxPages: options.maxPages ?? 200,\n concurrency: options.concurrency ?? 5,\n });\n siteData.blogSample = crawlResult.pages;\n siteData.crawlStats = {\n discovered: crawlResult.discoveredUrls.length,\n fetched: crawlResult.fetchedUrls.length,\n skipped: crawlResult.skippedUrls.length,\n elapsed: crawlResult.elapsed,\n };\n } else if (!options?.noMultiPage) {\n await fetchMultiPageData(siteData);\n }\n\n // Phase 4: Score all 26 criteria\n const results = auditSiteFromData(siteData);\n const overallScore = calculateOverallScore(results);\n const rawData = extractRawDataSummary(siteData);\n if (renderedWithHeadless) rawData.rendered_with_headless = true;\n\n // Phase 5: Build scorecard + detailed findings\n const scorecard = buildScorecard(results);\n const detailedFindings = buildDetailedFindings(results);\n\n // Phase 6: Generate narrative\n const verdict = generateVerdict(overallScore, scorecard, rawData, domain);\n const opportunities = generateOpportunities(scorecard, results);\n const pitchNumbers = generatePitchNumbers(overallScore, rawData, scorecard);\n const bottomLine = generateBottomLine(overallScore, opportunities, scorecard, domain);\n\n // Phase 7: Per-page analysis\n const pagesReviewed = analyzeAllPages(siteData);\n\n const elapsed = Math.round((Date.now() - startTime) / 100) / 10;\n\n return {\n site: domain,\n auditDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),\n auditor: 'AEORank',\n engine: 'instant',\n overallScore,\n verdict,\n scorecard,\n detailedFindings,\n opportunities,\n pitchNumbers,\n bottomLine,\n pagesReviewed,\n elapsed,\n ...(renderedWithHeadless && { renderedWithHeadless: true }),\n };\n}\n","/**\n * Link graph analysis for full-site AEO audits.\n * Builds an internal link graph, detects orphan pages, pillar pages,\n * hub pages, and topic clusters from crawled page data.\n */\n\nimport type { FetchResult, PageCategory } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface LinkEdge {\n source: string;\n target: string;\n anchorText: string;\n}\n\nexport interface PageNode {\n url: string;\n title: string;\n wordCount: number;\n category: PageCategory;\n inDegree: number;\n outDegree: number;\n depth: number;\n isPillar: boolean;\n isHub: boolean;\n isOrphan: boolean;\n}\n\nexport interface TopicCluster {\n pillarUrl: string;\n pillarTitle: string;\n spokes: string[];\n cohesion: number;\n}\n\nexport interface LinkGraphStats {\n totalPages: number;\n totalEdges: number;\n orphanPages: number;\n pillarPages: number;\n hubPages: number;\n avgDepth: number;\n maxDepth: number;\n clusters: number;\n}\n\nexport interface LinkGraph {\n nodes: Map<string, PageNode>;\n edges: LinkEdge[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\n// ─── Serialization (Map -> plain object for JSON) ───────────────────────────\n\nexport interface SerializedLinkGraph {\n nodes: PageNode[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\nexport function serializeLinkGraph(graph: LinkGraph): SerializedLinkGraph {\n return {\n nodes: Array.from(graph.nodes.values()),\n stats: graph.stats,\n clusters: graph.clusters,\n };\n}\n\n// ─── URL normalization (matches full-site-crawler.ts:421-429) ───────────────\n\nfunction normalizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return (parsed.origin + parsed.pathname.replace(/\\/+$/, '') + parsed.search).toLowerCase();\n } catch {\n return url.toLowerCase();\n }\n}\n\n// ─── Resource / skip-path patterns (same as full-site-crawler.ts) ───────────\n\nconst RESOURCE_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|woff|woff2|ttf|eot|mp4|mp3|webp|avif|zip|gz|tar|json)$/i;\n\nconst SKIP_PATH_PATTERNS = /^\\/(api|wp-admin|wp-json|static|assets|_next|auth|login|signup|cart|checkout|admin|feed|xmlrpc)\\b/i;\n\n// ─── HTML helpers (mirrors page-analyzer.ts) ────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Link extraction with anchor text ───────────────────────────────────────\n\n/**\n * Extract internal links from HTML with their anchor text.\n * Similar to extractInternalLinks in full-site-crawler.ts but returns LinkEdge[].\n */\nexport function extractLinksWithAnchors(\n html: string,\n sourceUrl: string,\n domain: string,\n): LinkEdge[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const edges: LinkEdge[] = [];\n const seen = new Set<string>();\n\n // Match <a ...>...</a> tags to get both href and anchor text\n const anchorRegex = /<a\\s[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = anchorRegex.exec(html)) !== null) {\n const href = match[1];\n const rawAnchor = match[2];\n\n if (!href || !href.trim()) continue;\n\n let fullUrl: string;\n\n if (href.startsWith('//')) {\n fullUrl = `https:${href}`;\n } else if (href.startsWith('/')) {\n if (href === '/' || href.startsWith('/#')) continue;\n fullUrl = `https://${domain}${href}`;\n } else if (href.startsWith('http')) {\n fullUrl = href;\n } else if (\n href.startsWith('#') || href.startsWith('?') ||\n href.startsWith('mailto:') || href.startsWith('tel:') ||\n href.startsWith('javascript:')\n ) {\n continue;\n } else {\n fullUrl = `https://${domain}/${href}`;\n }\n\n try {\n const parsed = new URL(fullUrl);\n const linkDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n\n parsed.hash = '';\n const path = parsed.pathname;\n\n if (path === '/' || path === '') continue;\n if (RESOURCE_EXTENSIONS.test(path)) continue;\n if (SKIP_PATH_PATTERNS.test(path)) continue;\n\n const normalized = normalizeUrl(fullUrl);\n const sourceNorm = normalizeUrl(sourceUrl);\n\n // Skip self-links\n if (normalized === sourceNorm) continue;\n\n // Deduplicate edges from same source to same target\n const edgeKey = `${sourceNorm}->${normalized}`;\n if (seen.has(edgeKey)) continue;\n seen.add(edgeKey);\n\n // Strip HTML tags from anchor text\n const anchorText = rawAnchor.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n\n edges.push({\n source: sourceNorm,\n target: normalized,\n anchorText,\n });\n } catch {\n continue;\n }\n }\n\n return edges;\n}\n\n// ─── BFS depth calculation ──────────────────────────────────────────────────\n\n/**\n * BFS from homepage to compute click depth for each node.\n * Unreachable pages get Infinity.\n */\nexport function calculateDepths(\n nodes: Map<string, PageNode>,\n adjacency: Map<string, Set<string>>,\n homepageUrl: string,\n): void {\n const homeNorm = normalizeUrl(homepageUrl);\n\n // Set all to Infinity first\n for (const node of nodes.values()) {\n node.depth = Infinity;\n }\n\n // Homepage gets depth 0\n const homeNode = nodes.get(homeNorm);\n if (homeNode) {\n homeNode.depth = 0;\n }\n\n // BFS\n const queue: string[] = [homeNorm];\n const visited = new Set<string>([homeNorm]);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentNode = nodes.get(current);\n if (!currentNode) continue;\n const nextDepth = currentNode.depth + 1;\n\n const neighbors = adjacency.get(current);\n if (!neighbors) continue;\n\n for (const neighbor of neighbors) {\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n const neighborNode = nodes.get(neighbor);\n if (neighborNode) {\n neighborNode.depth = nextDepth;\n queue.push(neighbor);\n }\n }\n }\n}\n\n// ─── Pillar detection ───────────────────────────────────────────────────────\n\nconst PILLAR_CATEGORIES: Set<PageCategory> = new Set(['blog', 'content', 'resources', 'docs']);\n\n/**\n * Detect pillar pages: long content with significant in/out linking.\n * wordCount >= 1500 AND inDegree >= 3 AND outDegree >= 3\n * AND category in blog/content/resources/docs AND not homepage (depth > 0).\n */\nexport function detectPillars(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isPillar =\n node.wordCount >= 1500 &&\n node.inDegree >= 3 &&\n node.outDegree >= 3 &&\n PILLAR_CATEGORIES.has(node.category) &&\n node.depth > 0;\n }\n}\n\n// ─── Hub detection ──────────────────────────────────────────────────────────\n\nconst HUB_CATEGORIES: Set<PageCategory> = new Set(['homepage', 'resources', 'docs']);\n\n/**\n * Detect hub pages: high outDegree navigation/index pages.\n * outDegree >= 10 with category homepage/resources/docs, OR outDegree >= 15.\n */\nexport function detectHubs(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isHub =\n (node.outDegree >= 10 && HUB_CATEGORIES.has(node.category)) ||\n node.outDegree >= 15;\n }\n}\n\n// ─── Cluster detection ──────────────────────────────────────────────────────\n\n/**\n * Detect topic clusters around pillar pages.\n * For each pillar, gather spokes (pages linked to/from pillar).\n * Cohesion = actual edges between cluster members / possible edges.\n * Minimum 2 spokes to form a cluster.\n */\nexport function detectClusters(\n nodes: Map<string, PageNode>,\n edges: LinkEdge[],\n): TopicCluster[] {\n const clusters: TopicCluster[] = [];\n\n // Build quick lookup for edges\n const edgeSet = new Set<string>();\n for (const edge of edges) {\n edgeSet.add(`${edge.source}->${edge.target}`);\n }\n\n for (const node of nodes.values()) {\n if (!node.isPillar) continue;\n\n const pillarNorm = normalizeUrl(node.url);\n const spokeSet = new Set<string>();\n\n // Find spokes: pages linked to/from this pillar\n for (const edge of edges) {\n if (edge.source === pillarNorm && nodes.has(edge.target)) {\n spokeSet.add(edge.target);\n }\n if (edge.target === pillarNorm && nodes.has(edge.source)) {\n spokeSet.add(edge.source);\n }\n }\n\n // Remove the pillar itself from spokes\n spokeSet.delete(pillarNorm);\n\n if (spokeSet.size < 2) continue;\n\n const spokes = Array.from(spokeSet);\n\n // Compute cohesion: edges between cluster members (pillar + spokes)\n const members = [pillarNorm, ...spokes];\n let actualEdges = 0;\n const possibleEdges = members.length * (members.length - 1); // directed\n\n for (const from of members) {\n for (const to of members) {\n if (from === to) continue;\n if (edgeSet.has(`${from}->${to}`)) {\n actualEdges++;\n }\n }\n }\n\n const cohesion = possibleEdges > 0 ? Math.round((actualEdges / possibleEdges) * 100) : 0;\n\n clusters.push({\n pillarUrl: node.url,\n pillarTitle: node.title,\n spokes,\n cohesion,\n });\n }\n\n return clusters;\n}\n\n// ─── Main graph builder ─────────────────────────────────────────────────────\n\n/**\n * Build a link graph from crawled pages.\n * @param pages - Array of FetchResult from full-site crawl\n * @param domain - The site domain\n * @param homepageUrl - Full URL of the homepage (e.g. https://example.com)\n */\nexport function buildLinkGraph(\n pages: FetchResult[],\n domain: string,\n homepageUrl: string,\n): LinkGraph {\n const nodes = new Map<string, PageNode>();\n const allEdges: LinkEdge[] = [];\n const adjacency = new Map<string, Set<string>>();\n const inDegreeMap = new Map<string, number>();\n\n // Phase 1: Create nodes from all pages\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const norm = normalizeUrl(url);\n\n if (nodes.has(norm)) continue;\n\n const title = extractTitle(page.text);\n const text = getTextContent(page.text);\n const wordCount = countWords(text);\n\n nodes.set(norm, {\n url: norm,\n title,\n wordCount,\n category: page.category || 'content',\n inDegree: 0,\n outDegree: 0,\n depth: Infinity,\n isPillar: false,\n isHub: false,\n isOrphan: false,\n });\n }\n\n // Phase 2: Extract edges\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const sourceNorm = normalizeUrl(url);\n\n const edges = extractLinksWithAnchors(page.text, url, domain);\n\n for (const edge of edges) {\n // Normalize edge source/target to match node keys\n const targetNorm = normalizeUrl(edge.target);\n\n // Only count edges to known pages\n if (!nodes.has(targetNorm)) continue;\n\n allEdges.push({\n source: sourceNorm,\n target: targetNorm,\n anchorText: edge.anchorText,\n });\n\n // Build adjacency\n if (!adjacency.has(sourceNorm)) {\n adjacency.set(sourceNorm, new Set());\n }\n adjacency.get(sourceNorm)!.add(targetNorm);\n\n // Track in-degree\n inDegreeMap.set(targetNorm, (inDegreeMap.get(targetNorm) || 0) + 1);\n }\n }\n\n // Phase 3: Set degrees\n for (const [url, node] of nodes) {\n node.inDegree = inDegreeMap.get(url) || 0;\n node.outDegree = adjacency.get(url)?.size || 0;\n }\n\n // Phase 4: BFS depths\n calculateDepths(nodes, adjacency, homepageUrl);\n\n // Phase 5: Detect pillars and hubs\n detectPillars(nodes);\n detectHubs(nodes);\n\n // Phase 6: Mark orphans (zero inbound links, not homepage)\n const homeNorm = normalizeUrl(homepageUrl);\n for (const [url, node] of nodes) {\n node.isOrphan = node.inDegree === 0 && url !== homeNorm;\n }\n\n // Phase 7: Detect clusters\n const clusters = detectClusters(nodes, allEdges);\n\n // Phase 8: Compute stats\n const depthValues = Array.from(nodes.values())\n .map(n => n.depth)\n .filter(d => d !== Infinity);\n const avgDepth = depthValues.length > 0\n ? Math.round((depthValues.reduce((s, d) => s + d, 0) / depthValues.length) * 10) / 10\n : 0;\n const maxDepth = depthValues.length > 0 ? Math.max(...depthValues) : 0;\n\n const stats: LinkGraphStats = {\n totalPages: nodes.size,\n totalEdges: allEdges.length,\n orphanPages: Array.from(nodes.values()).filter(n => n.isOrphan).length,\n pillarPages: Array.from(nodes.values()).filter(n => n.isPillar).length,\n hubPages: Array.from(nodes.values()).filter(n => n.isHub).length,\n avgDepth,\n maxDepth,\n clusters: clusters.length,\n };\n\n return { nodes, edges: allEdges, stats, clusters };\n}\n","/**\n * Fix Plan Engine - generates actionable, phased fix plans from audit scores.\n * Runs alongside the existing opportunities system (no breaking changes).\n * Uses criterion scores, optional per-page data, and optional link graph\n * to produce structured fix plans with code examples and dependency ordering.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { PageReview, PageCriterionScore } from './types.js';\nimport type { LinkGraph } from './link-graph.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface FixAction {\n id: string;\n criterion: string;\n criterionId: string;\n title: string;\n description: string;\n impact: 'critical' | 'high' | 'medium' | 'low';\n effort: 'trivial' | 'low' | 'medium' | 'high';\n impactScore: number;\n category: 'content' | 'structure' | 'discovery' | 'trust';\n steps: string[];\n codeExample?: string;\n successCriteria: string;\n dependsOn?: string[];\n affectedPages?: string[];\n pageCount?: number;\n}\n\nexport interface FixPhase {\n phase: number;\n title: string;\n description: string;\n fixes: FixAction[];\n estimatedImpact: number;\n}\n\nexport interface FixPlanSummary {\n criticalCount: number;\n highCount: number;\n mediumCount: number;\n lowCount: number;\n quickWinCount: number;\n topOpportunity: string;\n estimatedTotalEffort: string;\n}\n\nexport interface FixPlan {\n domain: string;\n generatedAt: string;\n overallScore: number;\n projectedScore: number;\n totalFixes: number;\n phases: FixPhase[];\n quickWins: FixAction[];\n summary: FixPlanSummary;\n}\n\n// ─── Criterion weights (mirrored from scoring.ts) ────────────────────────────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.05,\n query_answer_alignment: 0.05,\n faq_section: 0.04,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n definition_patterns: 0.02,\n visible_date_signal: 0.02,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\n// ─── Phase configuration ────────────────────────────────────────────────────\n\nconst PHASE_CONFIG: { phase: number; title: string; description: string; criteria: string[] }[] = [\n {\n phase: 1,\n title: 'Foundation',\n description: 'Discovery and structural fixes that enable AI crawlers to access and parse your content.',\n criteria: ['robots_txt', 'llms_txt', 'canonical_url', 'clean_html', 'sitemap_completeness'],\n },\n {\n phase: 2,\n title: 'Content',\n description: 'Content quality and format improvements that make your pages citable by AI engines.',\n criteria: [\n 'qa_content_format', 'faq_section', 'original_data', 'definition_patterns',\n 'direct_answer_density', 'fact_density', 'content_freshness',\n 'table_list_extractability', 'query_answer_alignment', 'visible_date_signal',\n 'topic_coherence', 'content_depth',\n ],\n },\n {\n phase: 3,\n title: 'Authority',\n description: 'Trust signals, schema depth, and semantic structure that establish credibility with AI engines.',\n criteria: [\n 'schema_markup', 'schema_coverage', 'speakable_schema', 'author_schema_depth',\n 'content_licensing', 'entity_consistency', 'semantic_html',\n ],\n },\n {\n phase: 4,\n title: 'Architecture',\n description: 'Site architecture, linking patterns, and publishing cadence that support long-term AI visibility.',\n criteria: ['internal_linking', 'content_velocity', 'content_cannibalization', 'rss_feed'],\n },\n];\n\n// ─── Criterion → category mapping ───────────────────────────────────────────\n\nconst CRITERION_CATEGORY: Record<string, FixAction['category']> = {\n robots_txt: 'discovery', llms_txt: 'discovery', canonical_url: 'structure',\n clean_html: 'structure', sitemap_completeness: 'discovery',\n qa_content_format: 'content', faq_section: 'content', original_data: 'content',\n definition_patterns: 'content', direct_answer_density: 'content', fact_density: 'content',\n content_freshness: 'content', table_list_extractability: 'content',\n query_answer_alignment: 'content', visible_date_signal: 'content',\n schema_markup: 'trust', schema_coverage: 'trust', speakable_schema: 'trust',\n author_schema_depth: 'trust', content_licensing: 'trust', entity_consistency: 'trust',\n semantic_html: 'structure', internal_linking: 'structure', content_velocity: 'content',\n content_cannibalization: 'content', rss_feed: 'discovery',\n topic_coherence: 'content', content_depth: 'content',\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction impactFromScore(score: number): FixAction['impact'] {\n if (score <= 3) return 'critical';\n if (score <= 5) return 'high';\n if (score <= 7) return 'medium';\n return 'low';\n}\n\nfunction effortForCriterion(criterion: string, score: number): FixAction['effort'] {\n // Low-effort criteria (file creation, config changes)\n const trivialCriteria = ['llms_txt', 'robots_txt', 'canonical_url', 'content_licensing', 'visible_date_signal'];\n const lowCriteria = ['rss_feed', 'sitemap_completeness', 'speakable_schema', 'author_schema_depth', 'semantic_html', 'definition_patterns', 'content_freshness'];\n const highCriteria = ['original_data', 'content_velocity', 'content_cannibalization'];\n\n if (trivialCriteria.includes(criterion)) return score <= 3 ? 'low' : 'trivial';\n if (lowCriteria.includes(criterion)) return score <= 3 ? 'medium' : 'low';\n if (highCriteria.includes(criterion)) return score <= 5 ? 'high' : 'medium';\n return score <= 3 ? 'medium' : 'low';\n}\n\nfunction getAffectedPages(\n criterion: string,\n pages?: PageReview[],\n threshold = 7,\n): string[] | undefined {\n if (!pages || pages.length === 0) return undefined;\n const affected = pages.filter(p => {\n const cs = p.criterionScores?.find((c: PageCriterionScore) => c.criterion === criterion);\n return cs && cs.score < threshold;\n });\n if (affected.length === 0) return undefined;\n return affected.map(p => p.url);\n}\n\nfunction effortToHours(effort: FixAction['effort']): number {\n switch (effort) {\n case 'trivial': return 0.5;\n case 'low': return 1;\n case 'medium': return 3;\n case 'high': return 8;\n }\n}\n\n// ─── Fix generator type ─────────────────────────────────────────────────────\n\ntype FixGenerator = (\n criterion: CriterionResult,\n pages?: PageReview[],\n linkGraph?: LinkGraph,\n) => FixAction[];\n\n// ─── 26 Fix Generators ─────────────────────────────────────────────────────\n\nconst FIX_GENERATORS: Record<string, FixGenerator> = {\n\n llms_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('llms_txt', c.score);\n const fixes: FixAction[] = [];\n if (c.score <= 6) {\n fixes.push({\n id: 'fix-llms-txt-create',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create /llms.txt file',\n description: 'Add a machine-readable llms.txt file at your domain root that describes your site, services, and key pages for AI engines.',\n impact, effort,\n impactScore: 0, // calculated later\n category: 'discovery',\n steps: [\n 'Create a file named llms.txt in your site root',\n 'Add site name, description, and core URLs in markdown format',\n 'Include key service/product pages and their descriptions',\n 'Deploy and verify access at yourdomain.com/llms.txt',\n ],\n codeExample: `# Site Name\\n> One-line site description\\n\\n## Core Pages\\n- [About](/about): Company overview\\n- [Services](/services): Service offerings\\n- [Blog](/blog): Latest articles\\n\\n## Key Topics\\n- Topic 1\\n- Topic 2`,\n successCriteria: '/llms.txt returns 200 with valid markdown content',\n });\n }\n if (c.score <= 3) {\n fixes.push({\n id: 'fix-llms-txt-full',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add llms-full.txt with extended content',\n description: 'Create a comprehensive llms-full.txt with detailed page descriptions, content summaries, and topic taxonomy.',\n impact: 'medium', effort: 'low',\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create llms-full.txt alongside llms.txt',\n 'Include full page descriptions with word counts',\n 'Add topic categories and content clusters',\n 'Link from llms.txt to llms-full.txt',\n ],\n successCriteria: '/llms-full.txt returns 200 with comprehensive site map',\n });\n }\n return fixes;\n },\n\n schema_markup: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_markup', c.score);\n const affected = getAffectedPages('schema_markup', pages);\n const fixes: FixAction[] = [{\n id: 'fix-schema-markup',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add JSON-LD structured data',\n description: 'Implement Organization, WebSite, and page-specific schema.org JSON-LD to help AI engines extract your content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Organization JSON-LD to your homepage with name, url, logo, sameAs',\n 'Add WebSite schema with SearchAction',\n 'Add page-specific schema (Article, Service, Product, FAQPage) to relevant pages',\n 'Validate with Google Rich Results Test',\n ],\n codeExample: `<script type=\"application/ld+json\">\\n{\\n \"@context\": \"https://schema.org\",\\n \"@type\": \"Organization\",\\n \"name\": \"Your Company\",\\n \"url\": \"https://example.com\",\\n \"logo\": \"https://example.com/logo.png\",\\n \"sameAs\": [\\n \"https://twitter.com/company\",\\n \"https://linkedin.com/company/company\"\\n ]\\n}\\n</script>`,\n successCriteria: 'Homepage and key pages have valid JSON-LD schema',\n dependsOn: ['fix-clean-html-structure'],\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n qa_content_format: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('qa_content_format', c.score);\n const affected = getAffectedPages('qa_content_format', pages);\n return [{\n id: 'fix-qa-format',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add question-based headings',\n description: 'Restructure content with H2/H3 question headings that match how users query AI assistants.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify top user questions for each page topic',\n 'Convert section headings to question format (What, How, Why, When)',\n 'Follow each question heading with a direct 2-3 sentence answer',\n 'Add a summary answer box at the top of long-form content',\n ],\n successCriteria: 'At least 50% of H2/H3 headings use question format',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n clean_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('clean_html', c.score);\n const affected = getAffectedPages('clean_html', pages);\n const fixes: FixAction[] = [{\n id: 'fix-clean-html-structure',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix HTML structure and meta tags',\n description: 'Ensure clean, well-structured HTML with proper meta tags, HTTPS, and parseable content for AI crawlers.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Enable HTTPS and redirect HTTP to HTTPS',\n 'Add proper <title>, meta description, and viewport meta tags',\n 'Fix HTML validation errors (unclosed tags, invalid nesting)',\n 'Ensure content is server-rendered (not client-side only)',\n ],\n successCriteria: 'Pages pass HTML validation with proper meta tags and HTTPS',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n entity_consistency: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_consistency', c.score);\n return [{\n id: 'fix-entity-consistency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Strengthen entity authority (NAP)',\n description: 'Add consistent name, address, phone (NAP) and sameAs links across all pages to strengthen entity recognition.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Ensure company name is consistent across all pages',\n 'Add Organization schema with full NAP details',\n 'Include sameAs links to social profiles and directories',\n 'Add logo and brand marks consistently',\n ],\n successCriteria: 'Organization schema present with consistent NAP on all pages',\n }];\n },\n\n robots_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('robots_txt', c.score);\n return [{\n id: 'fix-robots-txt',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Configure robots.txt for AI crawlers',\n description: 'Update robots.txt to explicitly allow AI crawlers and include sitemap directive.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create or update robots.txt at domain root',\n 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot',\n 'Include Sitemap directive pointing to sitemap.xml',\n 'Verify no accidental Disallow rules blocking content pages',\n ],\n codeExample: `User-agent: *\\nAllow: /\\n\\nUser-agent: GPTBot\\nAllow: /\\n\\nUser-agent: ClaudeBot\\nAllow: /\\n\\nUser-agent: PerplexityBot\\nAllow: /\\n\\nSitemap: https://example.com/sitemap.xml`,\n successCriteria: 'robots.txt returns 200 with AI crawler directives and Sitemap',\n }];\n },\n\n faq_section: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('faq_section', c.score);\n const affected = getAffectedPages('faq_section', pages);\n return [{\n id: 'fix-faq-section',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Build FAQ sections with schema',\n description: 'Create FAQ content with FAQPage schema markup on key pages to become a direct answer source for AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 8-10 most common customer questions per service area',\n 'Create dedicated FAQ page with categorized Q&A pairs',\n 'Add inline FAQ sections to key service/product pages',\n 'Implement FAQPage JSON-LD schema on all FAQ content',\n ],\n successCriteria: 'FAQ page exists with FAQPage schema, key pages have inline FAQ sections',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n original_data: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('original_data', c.score);\n const affected = getAffectedPages('original_data', pages);\n return [{\n id: 'fix-original-data',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add original data and case studies',\n description: 'Publish proprietary data, statistics, case studies, or research that AI engines cannot find elsewhere.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify internal data assets (customer metrics, case study results, survey data)',\n 'Create data-driven content with specific numbers and percentages',\n 'Publish case studies with measurable outcomes',\n 'Add comparison tables with proprietary benchmarks',\n ],\n successCriteria: 'At least 3 pages contain original data points not found elsewhere online',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n internal_linking: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('internal_linking', c.score);\n const fixes: FixAction[] = [];\n\n // Orphan pages fix\n if (linkGraph) {\n const orphans: string[] = [];\n linkGraph.nodes.forEach((node) => {\n if (node.isOrphan) orphans.push(node.url);\n });\n if (orphans.length > 0) {\n fixes.push({\n id: 'fix-internal-linking-orphans',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Link orphan pages into site navigation',\n description: `${orphans.length} pages have no incoming internal links. These are invisible to AI crawlers that follow links.`,\n impact: orphans.length > 5 ? 'critical' : 'high',\n effort: orphans.length > 10 ? 'medium' : 'low',\n impactScore: 0,\n category: 'structure',\n steps: [\n `Identify the ${orphans.length} orphan pages with zero incoming links`,\n 'Add contextual links from related content pages',\n 'Include orphan pages in navigation menus or footer links',\n 'Add \"Related Content\" sections on relevant pages',\n ],\n successCriteria: 'All content pages have at least 1 incoming internal link',\n affectedPages: orphans.slice(0, 20),\n pageCount: orphans.length,\n });\n }\n\n // Depth fix\n if (linkGraph.stats.maxDepth > 3) {\n fixes.push({\n id: 'fix-internal-linking-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce page depth for deep content',\n description: `Max depth is ${linkGraph.stats.maxDepth} clicks from homepage. AI crawlers rarely follow links beyond 3 levels.`,\n impact: 'medium',\n effort: 'medium',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify pages more than 3 clicks from the homepage',\n 'Add direct links from high-level pages to deep content',\n 'Consider flattening URL structure for key pages',\n 'Add hub pages that aggregate related deep content',\n ],\n successCriteria: 'All important content pages reachable within 3 clicks from homepage',\n });\n }\n\n // No clusters fix\n if (linkGraph.clusters.length === 0) {\n fixes.push({\n id: 'fix-internal-linking-clusters',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create topic clusters with pillar pages',\n description: 'No topic clusters detected. Organizing content into pillar-spoke clusters strengthens topical authority for AI engines.',\n impact: 'high',\n effort: 'high',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify 3-5 core topic areas for your business',\n 'Create comprehensive pillar pages (3000+ words) for each topic',\n 'Write 5-7 supporting articles per pillar linking back to pillar',\n 'Interlink supporting articles within each cluster',\n ],\n successCriteria: 'At least 2 topic clusters with pillar page and 5+ spoke pages',\n });\n }\n } else {\n // No link graph available - generic fix\n fixes.push({\n id: 'fix-internal-linking-generic',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve internal linking architecture',\n description: 'Strengthen internal linking with descriptive anchor text between related pages.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Audit current internal link structure',\n 'Add contextual links between related content pages',\n 'Ensure every key page is reachable within 3 clicks from homepage',\n 'Use descriptive anchor text instead of \"click here\" or \"read more\"',\n ],\n successCriteria: 'Key pages have 3+ incoming internal links with descriptive anchors',\n });\n }\n return fixes;\n },\n\n semantic_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('semantic_html', c.score);\n const affected = getAffectedPages('semantic_html', pages);\n return [{\n id: 'fix-semantic-html',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Implement semantic HTML5 elements',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Wrap main content in <main> element',\n 'Use <article> for self-contained content blocks',\n 'Add <nav> for navigation and <aside> for sidebars',\n 'Add lang attribute to <html> and ARIA labels for accessibility',\n ],\n successCriteria: 'Pages use semantic HTML5 elements with lang attribute',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_freshness: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_freshness', c.score);\n const affected = getAffectedPages('content_freshness', pages);\n return [{\n id: 'fix-content-freshness',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add content freshness signals',\n description: 'Include dateModified schema, visible dates, and recent content updates to signal freshness to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add datePublished and dateModified to Article schema',\n 'Display visible \"Last updated\" dates on content pages',\n 'Update stale content with current information',\n 'Add <time> elements with datetime attributes for all dates',\n ],\n successCriteria: 'Content pages show visible dates and have dateModified in schema',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n sitemap_completeness: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('sitemap_completeness', c.score);\n return [{\n id: 'fix-sitemap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create complete sitemap.xml',\n description: 'Generate a comprehensive sitemap with lastmod dates for all important pages.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Generate sitemap.xml listing all content pages',\n 'Include <lastmod> dates for each URL',\n 'Set <changefreq> and <priority> appropriately',\n 'Reference sitemap in robots.txt',\n ],\n successCriteria: 'sitemap.xml returns 200 with all content pages and lastmod dates',\n }];\n },\n\n rss_feed: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('rss_feed', c.score);\n return [{\n id: 'fix-rss-feed',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Deploy RSS/Atom feed',\n description: 'Add an RSS or Atom feed for your blog/news content to signal active publishing to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create RSS 2.0 or Atom feed for blog/news content',\n 'Include title, description, pubDate, and full content for each item',\n 'Add <link rel=\"alternate\" type=\"application/rss+xml\"> to page head',\n 'Auto-generate feed on each new publish',\n ],\n codeExample: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n <channel>\\n <title>Your Site Blog</title>\\n <link>https://example.com/blog</link>\\n <description>Latest articles</description>\\n <item>\\n <title>Article Title</title>\\n <link>https://example.com/blog/article</link>\\n <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>\\n <description>Article summary</description>\\n </item>\\n </channel>\\n</rss>`,\n successCriteria: 'RSS feed returns valid XML with recent content items',\n }];\n },\n\n table_list_extractability: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('table_list_extractability', c.score);\n const affected = getAffectedPages('table_list_extractability', pages);\n return [{\n id: 'fix-tables-lists',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add structured tables and lists',\n description: 'Use HTML tables for comparison data and lists for features, steps, and specifications.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify data suitable for table format (comparisons, pricing, specs)',\n 'Convert bullet points to proper <ul>/<ol> lists',\n 'Add comparison tables with <th> headers',\n 'Ensure tables have descriptive captions',\n ],\n successCriteria: 'Key pages contain at least one HTML table or structured list',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n definition_patterns: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('definition_patterns', c.score);\n const affected = getAffectedPages('definition_patterns', pages);\n return [{\n id: 'fix-definitions',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add definition-style content',\n description: 'Include clear definition patterns for key terms and concepts that AI engines can cite for \"what is\" queries.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify key industry terms your audience searches for',\n 'Write clear definitions using \"X is...\" or \"X refers to...\" patterns',\n 'Place definitions near the top of relevant pages',\n 'Consider a glossary page for comprehensive term coverage',\n ],\n successCriteria: 'Key pages contain definition patterns for relevant terms',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n direct_answer_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('direct_answer_density', c.score);\n const affected = getAffectedPages('direct_answer_density', pages);\n return [{\n id: 'fix-direct-answers',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add direct answer paragraphs',\n description: 'Write concise, standalone answer paragraphs after question headings for AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify question-format headings on each page',\n 'Write a 2-3 sentence direct answer immediately after each heading',\n 'Ensure answers are self-contained (don\\'t require context from other sections)',\n 'Use bold for key facts within answer paragraphs',\n ],\n successCriteria: 'Question headings are followed by direct, concise answer paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_licensing: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_licensing', c.score);\n return [{\n id: 'fix-content-licensing',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add ai.txt and content licensing',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to structured data.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create ai.txt at domain root with usage permissions',\n 'Specify allowed AI uses (training, citation, summarization)',\n 'Add license information to schema markup',\n 'Consider a content licensing page linked from footer',\n ],\n codeExample: `# ai.txt - AI Usage Policy for example.com\\n\\nUser-Agent: *\\nAllow: /blog/\\nAllow: /docs/\\n\\n# Permissions\\nTraining: yes\\nCitation: yes with attribution\\nSummarization: yes`,\n successCriteria: '/ai.txt returns 200 with clear AI usage permissions',\n }];\n },\n\n author_schema_depth: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('author_schema_depth', c.score);\n return [{\n id: 'fix-author-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Enhance author and expert schema',\n description: 'Add Person schema for content authors with credentials and sameAs links for E-E-A-T signals.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create author profile pages for content creators',\n 'Add Person schema with name, jobTitle, credentials, sameAs',\n 'Link articles to author profiles via schema author property',\n 'Include author bio and expertise on article pages',\n ],\n successCriteria: 'Articles have Person schema for authors with credentials',\n }];\n },\n\n fact_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('fact_density', c.score);\n const affected = getAffectedPages('fact_density', pages);\n return [{\n id: 'fix-fact-density',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase fact and data density',\n description: 'Add specific numbers, percentages, statistics, and data points that AI engines can cite.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Review content for vague claims and replace with specific data',\n 'Add statistics, percentages, and measurable outcomes',\n 'Include source citations for data points',\n 'Add data tables or comparison charts where appropriate',\n ],\n successCriteria: 'Key pages contain at least 3 specific data points per 500 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n canonical_url: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('canonical_url', c.score);\n const affected = getAffectedPages('canonical_url', pages);\n return [{\n id: 'fix-canonical-url',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix canonical URL strategy',\n description: 'Add rel=\"canonical\" tags to all pages to prevent duplicate content confusion.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Add <link rel=\"canonical\"> to every page pointing to preferred URL',\n 'Ensure canonical URLs use consistent scheme (https) and format',\n 'Handle www vs non-www with proper redirects',\n 'Set canonical for paginated content to the main page',\n ],\n codeExample: `<link rel=\"canonical\" href=\"https://example.com/page\" />`,\n successCriteria: 'All pages have rel=\"canonical\" pointing to the correct URL',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_velocity: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_velocity', c.score);\n return [{\n id: 'fix-content-velocity',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase publishing frequency',\n description: 'Establish a regular content publishing cadence to signal active, current information to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Set a publishing schedule (weekly or bi-weekly minimum)',\n 'Create a content calendar covering key topics',\n 'Update sitemap and RSS feed with each new publish',\n 'Refresh existing evergreen content with current data',\n ],\n successCriteria: 'At least 2 new or updated content pages per month with dated entries',\n }];\n },\n\n schema_coverage: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_coverage', c.score);\n return [{\n id: 'fix-schema-coverage',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Extend schema to inner pages',\n description: 'Add page-specific structured data beyond the homepage to articles, services, and product pages.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Article schema to blog/news pages',\n 'Add Service/Product schema to service/product pages',\n 'Add BreadcrumbList schema to all inner pages',\n 'Validate each page type with Rich Results Test',\n ],\n successCriteria: 'At least 50% of content pages have page-specific schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n speakable_schema: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('speakable_schema', c.score);\n return [{\n id: 'fix-speakable-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add SpeakableSpecification schema',\n description: 'Add Speakable schema to tell voice assistants which content sections are best for spoken answers.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Identify key paragraphs suitable for voice readout',\n 'Add SpeakableSpecification with CSS selectors to Article schema',\n 'Point speakable selectors to headline and summary paragraphs',\n 'Test with Google structured data testing tool',\n ],\n codeExample: `\"speakable\": {\\n \"@type\": \"SpeakableSpecification\",\\n \"cssSelector\": [\\n \".article-headline\",\\n \".article-summary\"\\n ]\\n}`,\n successCriteria: 'Article pages include SpeakableSpecification in schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n query_answer_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('query_answer_alignment', c.score);\n const affected = getAffectedPages('query_answer_alignment', pages);\n return [{\n id: 'fix-query-answer-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve query-answer alignment',\n description: 'Ensure question headings are followed by direct, concise answers in the first paragraph.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit question-format headings and their following paragraphs',\n 'Add direct answers in the first 1-2 sentences after each question heading',\n 'Remove filler text between question and answer',\n 'Ensure answers are self-contained and citable',\n ],\n successCriteria: 'Question headings have direct answer paragraphs within 50 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_cannibalization: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_cannibalization', c.score);\n const fixes: FixAction[] = [];\n\n if (linkGraph && linkGraph.clusters.length > 0) {\n // Check for low-cohesion clusters which may indicate overlapping content\n const lowCohesion = linkGraph.clusters.filter(cl => cl.cohesion < 50);\n if (lowCohesion.length > 0) {\n const affected = lowCohesion.flatMap(cl => [cl.pillarUrl, ...cl.spokes]);\n fixes.push({\n id: 'fix-content-cannibalization-overlap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Consolidate overlapping content',\n description: `${lowCohesion.length} content clusters have low cohesion, suggesting pages compete for the same topics.`,\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify pages targeting the same keywords or topics',\n 'Merge overlapping pages into single authoritative pages',\n 'Set up 301 redirects from merged pages to consolidated page',\n 'Differentiate remaining similar pages with distinct angles',\n ],\n successCriteria: 'No two pages target the same primary keyword or topic',\n affectedPages: affected.slice(0, 20),\n pageCount: affected.length,\n });\n }\n }\n\n if (fixes.length === 0) {\n const affected = getAffectedPages('content_cannibalization', pages);\n fixes.push({\n id: 'fix-content-cannibalization',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Resolve content cannibalization',\n description: 'Multiple pages may be targeting the same topics, diluting AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit pages for overlapping topic coverage',\n 'Consolidate similar pages into comprehensive single pages',\n 'Differentiate remaining pages with distinct angles and keywords',\n 'Add canonical tags to prevent duplicate content issues',\n ],\n successCriteria: 'Each topic is covered by a single authoritative page',\n affectedPages: affected,\n pageCount: affected?.length,\n });\n }\n\n return fixes;\n },\n\n visible_date_signal: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('visible_date_signal', c.score);\n const affected = getAffectedPages('visible_date_signal', pages);\n return [{\n id: 'fix-visible-dates',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add visible date signals',\n description: 'Add visible publication and modification dates using <time> elements for AI engine freshness assessment.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add visible \"Published\" and \"Last updated\" dates to content pages',\n 'Use <time> elements with datetime attributes',\n 'Ensure dates match dateModified in schema markup',\n 'Update dates when content is refreshed',\n ],\n codeExample: `<time datetime=\"2024-01-15\">January 15, 2024</time>`,\n successCriteria: 'Content pages show visible dates with <time> elements',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n topic_coherence: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('topic_coherence', c.score);\n return [{\n id: 'fix-topic-coherence',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Focus blog content on core expertise',\n description: 'Ensure blog content consistently covers your core topic areas. Scattered content across unrelated topics weakens AI engine authority signals.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 2-3 core expertise areas your brand is known for',\n 'Audit existing blog posts and remove or consolidate off-topic content',\n 'Create a content calendar focused on core topics',\n 'Use topic clusters: pillar pages linking to supporting articles within the same niche',\n ],\n successCriteria: '80%+ of blog content covers core expertise areas with consistent topic focus',\n }];\n },\n\n content_depth: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_depth', c.score);\n const affected = getAffectedPages('content_depth', pages);\n return [{\n id: 'fix-content-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase content depth and structure',\n description: 'Expand thin content with more detail, examples, and structured sections. AI engines prefer comprehensive articles with clear heading hierarchies.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Aim for 1000+ words per article with expert analysis and examples',\n 'Use H2/H3 subheadings every 200-300 words for clear structure',\n 'Add comparison tables, numbered steps, and data points',\n 'Remove or expand thin pages (under 300 words) that dilute site quality',\n ],\n successCriteria: 'Average article length exceeds 1000 words with 5+ subheadings per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n};\n\n// ─── Main function ──────────────────────────────────────────────────────────\n\nexport function generateFixPlan(\n domain: string,\n overallScore: number,\n criteria: CriterionResult[],\n pagesReviewed?: PageReview[],\n linkGraph?: LinkGraph,\n): FixPlan {\n // Step 1: Generate all fixes from criterion scores\n const allFixes: FixAction[] = [];\n\n for (const criterion of criteria) {\n const generator = FIX_GENERATORS[criterion.criterion];\n if (!generator) continue;\n\n const fixes = generator(criterion, pagesReviewed, linkGraph);\n for (const fix of fixes) {\n const weight = CRITERION_WEIGHTS[criterion.criterion] ?? 0.05;\n fix.impactScore = Math.round((10 - criterion.score) * weight * 100);\n allFixes.push(fix);\n }\n }\n\n // Step 2: Group into phases\n const phases: FixPhase[] = PHASE_CONFIG.map(config => {\n const phaseFixes = allFixes\n .filter(fix => config.criteria.includes(fix.criterionId))\n .sort((a, b) => b.impactScore - a.impactScore);\n\n return {\n phase: config.phase,\n title: config.title,\n description: config.description,\n fixes: phaseFixes,\n estimatedImpact: 0, // calculated after projected score\n };\n });\n\n // Step 3: Validate dependencies (move fixes if dependency is in later phase)\n for (const phase of phases) {\n for (const fix of phase.fixes) {\n if (!fix.dependsOn) continue;\n for (const depId of fix.dependsOn) {\n // Find which phase the dependency is in\n const depPhase = phases.find(p => p.fixes.some(f => f.id === depId));\n if (depPhase && depPhase.phase > phase.phase) {\n // Dependency is in a later phase - move this fix there\n phase.fixes = phase.fixes.filter(f => f.id !== fix.id);\n depPhase.fixes.push(fix);\n depPhase.fixes.sort((a, b) => b.impactScore - a.impactScore);\n break;\n }\n }\n }\n }\n\n // Step 4: Calculate projected score\n const totalWeight = Object.values(CRITERION_WEIGHTS).reduce((s, w) => s + w, 0);\n\n // Deduplicate: only count the best fix per criterion\n const bestDeltaPerCriterion = new Map<string, number>();\n for (const fix of allFixes) {\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n const delta = (improvement * weight / totalWeight) * 100;\n const existing = bestDeltaPerCriterion.get(fix.criterionId) ?? 0;\n if (delta > existing) bestDeltaPerCriterion.set(fix.criterionId, delta);\n }\n const scoreDelta = Array.from(bestDeltaPerCriterion.values()).reduce((s, d) => s + d, 0);\n\n const projectedScore = Math.min(100, Math.round(overallScore + scoreDelta));\n\n // Step 5: Calculate per-phase estimated impact\n for (const phase of phases) {\n let phaseImpact = 0;\n const seenCriteria = new Set<string>();\n for (const fix of phase.fixes) {\n if (seenCriteria.has(fix.criterionId)) continue;\n seenCriteria.add(fix.criterionId);\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n phaseImpact += (improvement * weight / totalWeight) * 100;\n }\n phase.estimatedImpact = Math.round(phaseImpact);\n }\n\n // Step 6: Extract quick wins\n const quickWins = allFixes.filter(\n f => (f.effort === 'trivial' || f.effort === 'low') && (f.impact === 'critical' || f.impact === 'high'),\n );\n\n // Step 7: Build summary\n const summary: FixPlanSummary = {\n criticalCount: allFixes.filter(f => f.impact === 'critical').length,\n highCount: allFixes.filter(f => f.impact === 'high').length,\n mediumCount: allFixes.filter(f => f.impact === 'medium').length,\n lowCount: allFixes.filter(f => f.impact === 'low').length,\n quickWinCount: quickWins.length,\n topOpportunity: allFixes.length > 0\n ? allFixes.sort((a, b) => b.impactScore - a.impactScore)[0].title\n : 'None',\n estimatedTotalEffort: formatEffort(allFixes.reduce((s, f) => s + effortToHours(f.effort), 0)),\n };\n\n return {\n domain,\n generatedAt: new Date().toISOString(),\n overallScore,\n projectedScore,\n totalFixes: allFixes.length,\n phases,\n quickWins,\n summary,\n };\n}\n\nfunction formatEffort(hours: number): string {\n if (hours < 1) return '<1h';\n return `~${Math.round(hours)}h`;\n}\n","/**\n * HTML report generator for AEORank audits.\n * Produces self-contained HTML with inline CSS - zero external dependencies.\n */\n\nimport type { AuditResult } from './audit.js';\n\n// ─── Score colors (matching monorepo lib/score-color.ts) ─────────────────────\n\nfunction scoreColor(score: number): string {\n if (score <= 40) return '#F44336';\n if (score <= 55) return '#FF9800';\n if (score <= 70) return '#FFC107';\n if (score <= 85) return '#4CAF50';\n return '#2E7D32';\n}\n\nfunction criterionColor(score: number): string {\n return scoreColor(score * 10);\n}\n\n// ─── Security: escape HTML entities ──────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n// ─── SVG score circle ────────────────────────────────────────────────────────\n\nfunction scoreCircleSvg(score: number, size = 160): string {\n const radius = (size - 16) / 2;\n const circumference = 2 * Math.PI * radius;\n const progress = (score / 100) * circumference;\n const color = scoreColor(score);\n\n return `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\">\n <circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${radius}\" fill=\"none\" stroke=\"#e0e0e0\" stroke-width=\"10\"/>\n <circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${radius}\" fill=\"none\" stroke=\"${color}\" stroke-width=\"10\"\n stroke-dasharray=\"${circumference}\" stroke-dashoffset=\"${circumference - progress}\"\n stroke-linecap=\"round\" transform=\"rotate(-90 ${size / 2} ${size / 2})\"/>\n <text x=\"${size / 2}\" y=\"${size / 2 + 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n font-size=\"42\" font-weight=\"700\" fill=\"${color}\">${score}</text>\n <text x=\"${size / 2}\" y=\"${size / 2 + 24}\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n font-size=\"13\" fill=\"#666\">/100</text>\n </svg>`;\n}\n\n// ─── Inline CSS ──────────────────────────────────────────────────────────────\n\nconst CSS = `\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1a1a1a; background: #f8f9fa; line-height: 1.6; }\n .container { max-width: 1100px; margin: 0 auto; padding: 32px 24px; }\n .header { text-align: center; margin-bottom: 40px; }\n .header h1 { font-size: 28px; font-weight: 700; margin-bottom: 4px; }\n .header .date { color: #666; font-size: 14px; }\n .score-section { display: flex; justify-content: center; margin: 24px 0 32px; }\n .verdict { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px 24px; margin-bottom: 32px; font-size: 15px; color: #333; }\n .section-title { font-size: 20px; font-weight: 700; margin-bottom: 16px; color: #1a1a1a; }\n .scorecard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .criterion-card { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 14px 16px; display: flex; align-items: center; gap: 12px; }\n .criterion-bar { width: 80px; height: 8px; background: #e0e0e0; border-radius: 4px; flex-shrink: 0; overflow: hidden; }\n .criterion-bar-fill { height: 100%; border-radius: 4px; }\n .criterion-score { font-size: 14px; font-weight: 700; min-width: 32px; text-align: center; }\n .criterion-name { font-size: 13px; flex: 1; }\n .criterion-status { font-size: 11px; color: #666; background: #f0f0f0; padding: 2px 8px; border-radius: 10px; white-space: nowrap; }\n table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; margin-bottom: 32px; }\n th { background: #f5f5f5; text-align: left; padding: 10px 14px; font-size: 13px; font-weight: 600; color: #555; border-bottom: 1px solid #e0e0e0; }\n td { padding: 10px 14px; font-size: 13px; border-bottom: 1px solid #f0f0f0; }\n .impact-badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; color: #fff; }\n .impact-critical { background: #F44336; }\n .impact-high { background: #FF5722; }\n .impact-quick-win { background: #4CAF50; }\n .impact-core-aeo { background: #2196F3; }\n .impact-medium { background: #FF9800; }\n .impact-low { background: #9E9E9E; }\n .impact-big-opportunity { background: #9C27B0; }\n .impact-measurement { background: #607D8B; }\n .bottom-line { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px 24px; margin-bottom: 32px; font-size: 15px; }\n .footer { text-align: center; color: #999; font-size: 12px; padding: 24px 0; border-top: 1px solid #e0e0e0; margin-top: 24px; }\n .compare-header { display: flex; justify-content: center; align-items: center; gap: 48px; margin-bottom: 32px; }\n .compare-site { text-align: center; }\n .compare-site h2 { font-size: 20px; margin-bottom: 8px; }\n .compare-vs { font-size: 24px; font-weight: 700; color: #999; }\n .delta-positive { color: #4CAF50; font-weight: 600; }\n .delta-negative { color: #F44336; font-weight: 600; }\n .delta-zero { color: #999; }\n .summary-box { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px 20px; margin-bottom: 24px; display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; }\n .summary-stat { text-align: center; }\n .summary-stat .num { font-size: 28px; font-weight: 700; }\n .summary-stat .label { font-size: 12px; color: #666; }\n @media print {\n body { background: #fff; }\n .container { padding: 0; }\n .criterion-card, .verdict, .bottom-line, table { break-inside: avoid; }\n }\n @media (max-width: 640px) {\n .scorecard-grid { grid-template-columns: 1fr; }\n .compare-header { flex-direction: column; gap: 16px; }\n }\n`;\n\n// ─── Impact badge class ──────────────────────────────────────────────────────\n\nfunction impactClass(impact: string): string {\n const key = impact.toLowerCase().replace(/\\s+/g, '-');\n return `impact-${key}`;\n}\n\n// ─── Single audit report ─────────────────────────────────────────────────────\n\nexport function generateHtmlReport(result: AuditResult): string {\n const domain = escapeHtml(result.site);\n const date = escapeHtml(result.auditDate);\n\n const scorecardCards = result.scorecard\n .map((item) => {\n const color = criterionColor(item.score);\n const width = item.score * 10;\n return `<div class=\"criterion-card\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${width}%;background:${color}\"></div></div>\n <span class=\"criterion-score\" style=\"color:${color}\">${item.score}/10</span>\n <span class=\"criterion-name\">${escapeHtml(item.criterion)}</span>\n <span class=\"criterion-status\">${escapeHtml(item.status)}</span>\n </div>`;\n })\n .join('\\n');\n\n const opportunityRows = result.opportunities\n .map((opp) => {\n const cls = impactClass(opp.impact);\n return `<tr>\n <td>${opp.id}</td>\n <td>${escapeHtml(opp.name)}</td>\n <td><span class=\"impact-badge ${cls}\">${escapeHtml(opp.impact)}</span></td>\n <td>${escapeHtml(opp.effort)}</td>\n <td>${escapeHtml(opp.description)}</td>\n </tr>`;\n })\n .join('\\n');\n\n const pagesReviewed = result.pagesReviewed || [];\n const pagesRows = pagesReviewed\n .map((page) => {\n const issueCount = page.issues.length;\n const strengthCount = page.strengths.length;\n const aeoDisplay = page.aeoScore != null ? `<span style=\"font-weight:600;color:${scoreColor(page.aeoScore)}\">${page.aeoScore}</span>` : '-';\n return `<tr>\n <td>${escapeHtml(page.url)}</td>\n <td>${escapeHtml(page.category)}</td>\n <td>${page.wordCount}</td>\n <td>${aeoDisplay}</td>\n <td>${issueCount}</td>\n <td>${strengthCount}</td>\n </tr>`;\n })\n .join('\\n');\n\n const scoredPages = pagesReviewed.filter(p => p.aeoScore != null);\n const avgPageScore = scoredPages.length > 0\n ? Math.round(scoredPages.reduce((sum, p) => sum + p.aeoScore!, 0) / scoredPages.length)\n : null;\n\n const now = new Date().toISOString();\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>AEORank Report - ${domain}</title>\n <style>${CSS}</style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>${domain}</h1>\n <div class=\"date\">AEO Audit - ${date}</div>\n </div>\n\n <div class=\"score-section\">\n ${scoreCircleSvg(result.overallScore)}\n </div>\n\n <div class=\"verdict\">${escapeHtml(result.verdict)}</div>\n\n <h2 class=\"section-title\">Scorecard (26 Criteria)</h2>\n <div class=\"scorecard-grid\">\n ${scorecardCards}\n </div>\n\n ${result.opportunities.length > 0 ? `\n <h2 class=\"section-title\">Opportunities (${result.opportunities.length})</h2>\n <table>\n <thead><tr><th>#</th><th>Opportunity</th><th>Impact</th><th>Effort</th><th>Description</th></tr></thead>\n <tbody>${opportunityRows}</tbody>\n </table>\n ` : ''}\n\n ${pagesReviewed.length > 0 ? `\n <h2 class=\"section-title\">Pages Reviewed (${pagesReviewed.length})</h2>\n ${avgPageScore != null ? `<div class=\"summary-box\"><div class=\"summary-stat\"><div class=\"num\" style=\"color:${scoreColor(avgPageScore)}\">${avgPageScore}</div><div class=\"label\">Avg Page AEO Score</div></div></div>` : ''}\n <table>\n <thead><tr><th>URL</th><th>Category</th><th>Words</th><th>AEO Score</th><th>Issues</th><th>Strengths</th></tr></thead>\n <tbody>${pagesRows}</tbody>\n </table>\n ` : ''}\n\n <div class=\"bottom-line\"><strong>Bottom line:</strong> ${escapeHtml(result.bottomLine)}</div>\n\n <div class=\"footer\">Generated by AEORank - ${now}</div>\n </div>\n</body>\n</html>`;\n}\n\n// ─── Comparison report types (imported from compare.ts at runtime) ───────────\n\ninterface CriterionComparisonLike {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\ninterface ComparisonResultLike {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparisonLike[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n// ─── Comparison HTML report ──────────────────────────────────────────────────\n\nexport function generateComparisonHtmlReport(result: ComparisonResultLike): string {\n const domainA = escapeHtml(result.siteA.site);\n const domainB = escapeHtml(result.siteB.site);\n const scoreA = result.siteA.overallScore;\n const scoreB = result.siteB.overallScore;\n\n const criteriaRows = result.comparison.criteria\n .map((c) => {\n const colorA = criterionColor(c.scoreA);\n const colorB = criterionColor(c.scoreB);\n const widthA = c.scoreA * 10;\n const widthB = c.scoreB * 10;\n let deltaHtml: string;\n if (c.delta > 0) deltaHtml = `<span class=\"delta-positive\">+${c.delta}</span>`;\n else if (c.delta < 0) deltaHtml = `<span class=\"delta-negative\">${c.delta}</span>`;\n else deltaHtml = `<span class=\"delta-zero\">0</span>`;\n\n return `<tr>\n <td>${c.id}</td>\n <td>${escapeHtml(c.criterion)}</td>\n <td>\n <div style=\"display:flex;align-items:center;gap:8px\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${widthA}%;background:${colorA}\"></div></div>\n <span style=\"color:${colorA};font-weight:600\">${c.scoreA}</span>\n </div>\n </td>\n <td>\n <div style=\"display:flex;align-items:center;gap:8px\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${widthB}%;background:${colorB}\"></div></div>\n <span style=\"color:${colorB};font-weight:600\">${c.scoreB}</span>\n </div>\n </td>\n <td style=\"text-align:center\">${deltaHtml}</td>\n </tr>`;\n })\n .join('\\n');\n\n const advantagesA = result.comparison.siteAAdvantages.length;\n const advantagesB = result.comparison.siteBAdvantages.length;\n const tied = result.comparison.tied.length;\n\n const now = new Date().toISOString();\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>AEORank Comparison - ${domainA} vs ${domainB}</title>\n <style>${CSS}</style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>AEO Comparison</h1>\n </div>\n\n <div class=\"compare-header\">\n <div class=\"compare-site\">\n <h2>${domainA}</h2>\n ${scoreCircleSvg(scoreA, 120)}\n </div>\n <div class=\"compare-vs\">vs</div>\n <div class=\"compare-site\">\n <h2>${domainB}</h2>\n ${scoreCircleSvg(scoreB, 120)}\n </div>\n </div>\n\n <div class=\"summary-box\">\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:${scoreColor(scoreA)}\">${advantagesA}</div>\n <div class=\"label\">${domainA} leads</div>\n </div>\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:${scoreColor(scoreB)}\">${advantagesB}</div>\n <div class=\"label\">${domainB} leads</div>\n </div>\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:#999\">${tied}</div>\n <div class=\"label\">Tied</div>\n </div>\n </div>\n\n <h2 class=\"section-title\">Per-Criterion Comparison</h2>\n <table>\n <thead>\n <tr>\n <th>#</th>\n <th>Criterion</th>\n <th>${domainA}</th>\n <th>${domainB}</th>\n <th>Delta</th>\n </tr>\n </thead>\n <tbody>${criteriaRows}</tbody>\n </table>\n\n ${result.comparison.siteAAdvantages.length > 0 ? `\n <h2 class=\"section-title\">${domainA} Advantages</h2>\n <div class=\"verdict\">${result.comparison.siteAAdvantages.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n ${result.comparison.siteBAdvantages.length > 0 ? `\n <h2 class=\"section-title\">${domainB} Advantages</h2>\n <div class=\"verdict\">${result.comparison.siteBAdvantages.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n ${result.comparison.tied.length > 0 ? `\n <h2 class=\"section-title\">Tied Criteria</h2>\n <div class=\"verdict\">${result.comparison.tied.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n <div class=\"footer\">Generated by AEORank - ${now}</div>\n </div>\n</body>\n</html>`;\n}\n","/**\n * Comparison mode - run two audits in parallel and compute per-criterion deltas.\n */\n\nimport { audit } from './audit.js';\nimport type { AuditOptions, AuditResult } from './audit.js';\n\nexport interface CriterionComparison {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\nexport interface ComparisonResult {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparison[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n/**\n * Audit two domains in parallel and build a per-criterion comparison.\n */\nexport async function compare(\n domainA: string,\n domainB: string,\n options?: AuditOptions,\n): Promise<ComparisonResult> {\n const [siteA, siteB] = await Promise.all([\n audit(domainA, options),\n audit(domainB, options),\n ]);\n\n const criteria: CriterionComparison[] = [];\n const siteAAdvantages: string[] = [];\n const siteBAdvantages: string[] = [];\n const tied: string[] = [];\n\n // Zip scorecards by id (both should have 26 items in the same order)\n for (let i = 0; i < siteA.scorecard.length; i++) {\n const a = siteA.scorecard[i];\n const b = siteB.scorecard[i];\n if (!a || !b) continue;\n\n const delta = a.score - b.score;\n\n criteria.push({\n id: a.id,\n criterion: a.criterion,\n scoreA: a.score,\n scoreB: b.score,\n delta,\n statusA: a.status,\n statusB: b.status,\n });\n\n if (delta > 0) siteAAdvantages.push(a.criterion);\n else if (delta < 0) siteBAdvantages.push(a.criterion);\n else tied.push(a.criterion);\n }\n\n return {\n siteA,\n siteB,\n comparison: {\n scoreDelta: siteA.overallScore - siteB.overallScore,\n criteria,\n siteAAdvantages,\n siteBAdvantages,\n tied,\n },\n };\n}\n"],"mappings":"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","names":["text","cap","extractQuestionHeadings","getTextContent","countWords","crawlFullSite","extractTitle","getTextContent","countWords","CRITERION_WEIGHTS","CRITERION_WEIGHTS"]}
1
+ {"version":3,"sources":["../src/parked-domain.ts","../src/site-crawler.ts","../src/scoring.ts","../src/headless-fetch.ts","../src/scorecard-builder.ts","../src/narrative-generator.ts","../src/multi-page-fetcher.ts","../src/page-scorer.ts","../src/page-analyzer.ts","../src/audit.ts","../src/link-graph.ts","../src/fix-engine.ts","../src/html-report.ts","../src/compare.ts"],"sourcesContent":["/**\n * Parked domain detection.\n * Vendored from @aeo/queue/redirect-check (pure functions, no network).\n */\n\nexport interface ParkedDomainResult {\n isParked: boolean;\n reason?: string;\n}\n\n/** Known parking paths used by domain parking services */\nconst PARKING_PATHS = ['/lander', '/parking', '/park', '/sedoparking'];\n\n/** Known parking service domains found in page HTML (scripts, iframes, links) */\nconst PARKING_SERVICE_DOMAINS = [\n 'sedoparking.com',\n 'parkingcrew.net',\n 'bodis.com',\n 'dsparking.com',\n 'hugedomains.com',\n 'afternic.com',\n 'dan.com',\n 'undeveloped.com',\n 'domainmarket.com',\n 'sav.com',\n 'domaincontrol.com',\n 'above.com',\n 'domainlore.com',\n 'domainnamesales.com',\n 'brandbucket.com',\n 'squadhelp.com',\n 'godaddy.com/domainsearch',\n];\n\n/** Parking-specific text patterns (case-insensitive) */\nconst PARKING_TEXT_PATTERNS = [\n /\\bbuy this domain\\b/i,\n /\\bdomain is for sale\\b/i,\n /\\bthis domain may be for sale\\b/i,\n /\\bdomain for sale\\b/i,\n /\\bthis domain name is available\\b/i,\n /\\bparked by/i,\n /\\bthis page is parked/i,\n /\\bdomain has expired/i,\n /\\bthis domain has been registered/i,\n /\\bmake an offer on this domain\\b/i,\n /\\bget this domain\\b/i,\n /\\bacquire this domain\\b/i,\n];\n\nfunction detectParkingRedirect(bodySnippet: string): string | null {\n const relativeRedirect = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"](\\/[^'\"]*)['\"]/i,\n );\n if (!relativeRedirect) return null;\n const path = relativeRedirect[2].toLowerCase().replace(/[?#].*/, '');\n if (PARKING_PATHS.includes(path)) {\n return `js-redirect to ${relativeRedirect[2]}`;\n }\n return null;\n}\n\nfunction detectParkingService(bodySnippet: string): string | null {\n const lower = bodySnippet.toLowerCase();\n for (const service of PARKING_SERVICE_DOMAINS) {\n if (lower.includes(service)) {\n return `parking service: ${service}`;\n }\n }\n return null;\n}\n\nfunction detectParkingText(bodySnippet: string): string | null {\n for (const pattern of PARKING_TEXT_PATTERNS) {\n if (pattern.test(bodySnippet)) {\n return `parking text: ${bodySnippet.match(pattern)?.[0]}`;\n }\n }\n return null;\n}\n\n/**\n * Detect if a page is a parked/lost/for-sale domain.\n * Pure function - no network calls.\n */\nexport function detectParkedDomain(bodySnippet: string): ParkedDomainResult {\n const parkingRedirect = detectParkingRedirect(bodySnippet);\n if (parkingRedirect) return { isParked: true, reason: parkingRedirect };\n\n const parkingService = detectParkingService(bodySnippet);\n if (parkingService) return { isParked: true, reason: parkingService };\n\n const parkingText = detectParkingText(bodySnippet);\n if (parkingText) return { isParked: true, reason: parkingText };\n\n return { isParked: false };\n}\n","import type { AuditFinding, AuditStatus, Priority } from './types.js';\nimport { detectParkedDomain } from './parked-domain.js';\n\nexport interface CriterionResult {\n criterion: string;\n criterion_label: string;\n score: number;\n status: AuditStatus;\n findings: AuditFinding[];\n fix_priority: Priority;\n}\n\n// ─── Pre-fetched site data (one fetch per URL, reused across all criteria) ───\n\nexport type PageCategory = 'homepage' | 'blog' | 'about' | 'pricing' | 'services'\n | 'contact' | 'team' | 'resources' | 'docs' | 'cases' | 'faq' | 'content';\n\nexport interface FetchResult {\n text: string;\n status: number;\n finalUrl?: string;\n category?: PageCategory;\n}\n\nexport interface SiteData {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage: FetchResult | null;\n llmsTxt: FetchResult | null;\n robotsTxt: FetchResult | null;\n faqPage: FetchResult | null;\n sitemapXml: FetchResult | null;\n rssFeed: FetchResult | null;\n aiTxt: FetchResult | null;\n /** Set when homepage redirects to a different (non-brand) domain */\n redirectedTo: string | null;\n /** Set when homepage is a parked/for-sale/lost domain */\n parkedReason: string | null;\n /** Sampled blog/content pages from sitemap (up to 50) */\n blogSample?: FetchResult[];\n /** Full-crawl statistics (set when --full-crawl is used) */\n crawlStats?: {\n discovered: number;\n fetched: number;\n skipped: number;\n elapsed: number;\n };\n /** Link graph from full-site crawl */\n linkGraph?: import('./link-graph.js').LinkGraph;\n}\n\n// Raw data summary for AI narrative generation\nexport interface RawDataSummary {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage_length: number;\n homepage_text_length: number;\n has_https: boolean;\n llms_txt_status: number | null;\n llms_txt_length: number;\n robots_txt_status: number | null;\n robots_txt_snippet: string;\n robots_txt_ai_crawlers: string[];\n robots_txt_blocked_crawlers: string[];\n schema_types_found: string[];\n schema_block_count: number;\n faq_page_status: number | null;\n faq_page_length: number;\n sitemap_status: number | null;\n internal_link_count: number;\n external_link_count: number;\n question_headings_count: number;\n h1_count: number;\n has_meta_description: boolean;\n has_title: boolean;\n has_phone: boolean;\n has_address: boolean;\n has_org_schema: boolean;\n has_social_links: boolean;\n semantic_elements_found: string[];\n img_count: number;\n img_with_alt_count: number;\n has_lang_attr: boolean;\n has_aria: boolean;\n has_breadcrumbs: boolean;\n has_nav: boolean;\n has_footer: boolean;\n has_case_studies: boolean;\n has_statistics: boolean;\n has_expert_attribution: boolean;\n has_blog_section: boolean;\n // New criteria fields\n has_date_modified_schema: boolean;\n time_element_count: number;\n sitemap_url_count: number;\n has_rss_feed: boolean;\n table_count: number;\n ordered_list_count: number;\n unordered_list_count: number;\n definition_pattern_count: number;\n has_ai_txt: boolean;\n has_person_schema: boolean;\n fact_data_point_count: number;\n has_canonical: boolean;\n has_license_schema: boolean;\n sitemap_recent_lastmod_count: number;\n rendered_with_headless?: boolean;\n // Speakable schema fields\n has_speakable_schema: boolean;\n speakable_selector_count: number;\n // Blog sample fields\n blog_sample_count: number;\n blog_sample_urls: string[];\n blog_sample_schema_types: string[];\n blog_sample_question_headings: number;\n blog_sample_faq_schema_found: boolean;\n // Criteria 24-26 fields\n question_heading_answer_rate: number; // -1 = N/A, 0-100 = %\n question_heading_total: number;\n cannibalizing_pairs_count: number;\n page_titles_sampled: number;\n has_visible_date: boolean;\n has_schema_date_in_ld: boolean;\n date_modified_recency_days: number | null;\n // Full-crawl stats\n crawl_discovered: number;\n crawl_fetched: number;\n crawl_skipped: number;\n}\n\nasync function fetchText(url: string): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(15000),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n const text = await res.text();\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n/** Extract bare domain from URL (no protocol, www, port, path) */\nfunction extractDomain(url: string): string {\n return url.replace(/^https?:\\/\\//, '').replace(/\\/.*/, '').replace(/:[0-9]+$/, '').replace(/^www\\./, '').toLowerCase();\n}\n\n/** Extract brand name from domain (everything before TLD) */\nfunction extractBrandName(domain: string): string {\n const parts = domain.split('.');\n const twoPartTlds = ['co.uk', 'com.au', 'co.jp', 'com.br', 'co.nz', 'co.in'];\n const lastTwo = parts.slice(-2).join('.');\n if (twoPartTlds.includes(lastTwo) && parts.length > 2) {\n return parts.slice(0, -2).join('.');\n }\n return parts.length > 1 ? parts.slice(0, -1).join('.') : domain;\n}\n\n/** Check if redirect target is a different brand (hijacked domain) */\nfunction detectCrossDomainRedirect(originalDomain: string, homepage: FetchResult): string | null {\n if (!homepage.finalUrl) return null;\n const finalDomain = extractDomain(homepage.finalUrl);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n finalDomain === cleanOriginal ||\n finalDomain === `www.${cleanOriginal}` ||\n extractBrandName(finalDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return finalDomain;\n}\n\n/** Detect JS-based cross-domain redirect in HTML body */\nfunction detectJsRedirect(bodySnippet: string, originalDomain: string): string | null {\n const jsMatch = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"]https?:\\/\\/([^'\"]+)['\"]/i,\n );\n if (!jsMatch) return null;\n const jsDomain = extractDomain('https://' + jsMatch[2]);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n jsDomain === cleanOriginal ||\n jsDomain === `www.${cleanOriginal}` ||\n extractBrandName(jsDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return jsDomain;\n}\n\n/** Detect HTML served for plain-text URLs (e.g. catch-all routes returning 200 for /ai.txt) */\nfunction isHtmlResponse(result: FetchResult | null): boolean {\n if (!result || result.status !== 200) return false;\n const trimmed = result.text.trimStart().slice(0, 200).toLowerCase();\n return trimmed.startsWith('<!doctype html') || trimmed.startsWith('<html') || /<head[\\s>]/i.test(trimmed);\n}\n\n/**\n * Fetches all site data in parallel with HTTPS/HTTP fallback.\n * Single entry point for all HTTP requests - no redundant fetches.\n */\nexport async function prefetchSiteData(domain: string): Promise<SiteData> {\n // Step 1: Detect protocol (HTTPS first, fallback to HTTP)\n let protocol: 'https' | 'http' | null = null;\n let homepage: FetchResult | null = null;\n\n homepage = await fetchText(`https://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'https';\n } else {\n homepage = await fetchText(`http://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'http';\n }\n }\n\n if (!protocol) {\n return { domain, protocol: null, homepage: null, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: null, blogSample: [] };\n }\n\n // Check for cross-domain redirect (hijacked/expired domains)\n const httpRedirect = homepage ? detectCrossDomainRedirect(domain, homepage) : null;\n const jsRedirect = homepage ? detectJsRedirect(homepage.text.slice(0, 8192), domain) : null;\n const redirectedTo = httpRedirect || jsRedirect;\n\n if (redirectedTo) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo, parkedReason: null, blogSample: [] };\n }\n\n // Check for parked/lost/for-sale domains\n const parkedResult = homepage ? detectParkedDomain(homepage.text.slice(0, 8192)) : { isParked: false };\n if (parkedResult.isParked) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: parkedResult.reason || 'parked', blogSample: [] };\n }\n\n const baseUrl = `${protocol}://${domain}`;\n\n // Step 2: Fetch all other resources in parallel\n const [llmsTxt, robotsTxt, faqPage, sitemapXml, aiTxt] = await Promise.all([\n fetchText(`${baseUrl}/llms.txt`),\n fetchText(`${baseUrl}/robots.txt`),\n fetchText(`${baseUrl}/faq`).then(async (result) => {\n if (result && result.status === 200) return result;\n // Fallback chain for FAQ page variants\n for (const path of ['/frequently-asked-questions', '/help', '/support', '/help-center']) {\n const fallback = await fetchText(`${baseUrl}${path}`);\n if (fallback && fallback.status === 200) return fallback;\n }\n return result;\n }),\n fetchText(`${baseUrl}/sitemap.xml`),\n fetchText(`${baseUrl}/ai.txt`),\n ]);\n\n // Step 3: Discover RSS feed URL from homepage, then fetch it\n let rssFeed: FetchResult | null = null;\n if (homepage) {\n const rssLinkMatch = homepage.text.match(/<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"[^>]*href=\"([^\"]*)\"[^>]*>/i);\n if (rssLinkMatch) {\n const rssUrl = rssLinkMatch[1].startsWith('http') ? rssLinkMatch[1] : `${baseUrl}${rssLinkMatch[1]}`;\n rssFeed = await fetchText(rssUrl);\n }\n if (!rssFeed || rssFeed.status !== 200) {\n // Fallback: try common RSS paths\n for (const path of ['/feed', '/rss.xml', '/feed.xml']) {\n rssFeed = await fetchText(`${baseUrl}${path}`);\n if (rssFeed && rssFeed.status === 200 && (rssFeed.text.includes('<rss') || rssFeed.text.includes('<feed') || rssFeed.text.includes('<channel'))) break;\n rssFeed = null;\n }\n }\n }\n\n // Step 4: If sitemapindex, fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries\n if (sitemapXml && sitemapXml.status === 200 && sitemapXml.text.includes('<sitemapindex')) {\n const subUrls = extractAllSubSitemapUrls(sitemapXml.text, 5);\n if (subUrls.length > 0) {\n const subResults = await Promise.all(subUrls.map(u => fetchText(u)));\n for (const sub of subResults) {\n if (sub && sub.status === 200) {\n sitemapXml.text += '\\n' + sub.text;\n }\n }\n }\n }\n\n // Step 5: Sample blog pages from sitemap (up to 5)\n let blogSample: FetchResult[] = [];\n if (sitemapXml && sitemapXml.status === 200) {\n const sitemapForBlog = sitemapXml.text;\n\n const blogUrls = extractBlogUrlsFromSitemap(sitemapForBlog, domain, 50);\n if (blogUrls.length > 0) {\n const fetched = await Promise.all(blogUrls.map(url => fetchText(url)));\n blogSample = fetched.filter((r): r is FetchResult =>\n r !== null && r.status === 200 && r.text.length > 500\n );\n // Tag blog sample pages\n for (const page of blogSample) {\n page.category = 'blog';\n }\n }\n }\n\n // Tag homepage\n if (homepage) homepage.category = 'homepage';\n\n return { domain, protocol, homepage, llmsTxt, robotsTxt, faqPage, sitemapXml, rssFeed, aiTxt, redirectedTo: null, parkedReason: null, blogSample };\n}\n\n// ─── Blog sample helpers ─────────────────────────────────────────────────────\n\n/** Strip executable script/style tags to avoid inline JS polluting text analysis.\n * Preserves <script type=\"application/ld+json\"> (JSON-LD structured data). */\nfunction stripScripts(html: string): string {\n return html\n .replace(/<script(?![^>]*type\\s*=\\s*[\"']application\\/ld\\+json[\"'])[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '');\n}\n\n/** Concatenate homepage + blog sample HTML for combined analysis */\nfunction getCombinedHtml(data: SiteData): string {\n const parts = [stripScripts(data.homepage?.text || '')];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n parts.push(stripScripts(page.text));\n }\n }\n return parts.join('\\n');\n}\n\n/** Get blog-only HTML concatenated */\nfunction getBlogHtml(data: SiteData): string {\n if (!data.blogSample || data.blogSample.length === 0) return '';\n return data.blogSample.map(p => stripScripts(p.text)).join('\\n');\n}\n\n// ─── Criterion checks (all use pre-fetched SiteData) ────────────────────────\n\n// Criterion 1: llms.txt\nfunction checkLlmsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.llmsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n const statusNote = result ? (isHtmlResponse(result) ? 'HTML page served (not a valid text file)' : `HTTP ${result.status}`) : 'connection failed';\n findings.push({ severity: 'critical', detail: `No llms.txt file found at ${data.protocol}://${data.domain}/llms.txt (${statusNote})`, fix: 'Create a /llms.txt file that describes your site, services, and key pages in markdown format' });\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: 0, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text;\n let score = 4; // exists\n\n if (text.length < 100) {\n findings.push({ severity: 'medium', detail: `llms.txt exists but is very short (${text.length} characters)`, fix: 'Add comprehensive description of your services, team, and key content' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: `llms.txt file found (${text.length} characters)` });\n }\n\n if (text.includes('#') || text.includes('##')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt uses markdown headings for structure' });\n } else {\n findings.push({ severity: 'low', detail: 'llms.txt lacks markdown structure', fix: 'Add headings (# About, ## Services, etc.) for better LLM parsing' });\n }\n\n if (/https?:\\/\\//.test(text)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt includes URLs to key pages' });\n } else {\n findings.push({ severity: 'medium', detail: 'llms.txt does not link to key pages', fix: 'Add URLs to your most important pages (services, about, FAQ)' });\n }\n\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: Math.min(10, score), status: score >= 7 ? 'pass' : 'partial', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// Criterion 2: Schema Markup\nfunction checkSchemaMarkup(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage to check schema markup' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD structured data found on homepage', fix: 'Add Organization, LocalBusiness, or WebSite schema in a <script type=\"application/ld+json\"> tag' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${ldJsonMatches.length} JSON-LD block(s) on homepage` });\n\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundTypes: string[] = [];\n\n for (const type of schemaTypes) {\n if (allSchemaText.includes(`\"${type}\"`) || allSchemaText.includes(`\"@type\":\"${type}\"`)) {\n foundTypes.push(type);\n }\n }\n\n if (foundTypes.length > 0) {\n score += Math.min(4, foundTypes.length * 2);\n findings.push({ severity: 'info', detail: `Schema types found: ${foundTypes.join(', ')}` });\n }\n\n if (!foundTypes.includes('organization') && !foundTypes.includes('localbusiness')) {\n findings.push({ severity: 'high', detail: 'Missing Organization or LocalBusiness schema', fix: 'Add Organization schema with name, url, logo, contactPoint, and sameAs properties' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'Organization or LocalBusiness schema found' });\n }\n\n if (!foundTypes.includes('faqpage')) {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema found', fix: 'Add FAQPage schema on pages with FAQ content' });\n } else {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema markup present' });\n }\n\n // Blog sample enhancement: additional schema types from blog posts\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = getBlogHtml(data);\n const blogLdJson = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (blogLdJson.length > 0) {\n const blogSchemaText = blogLdJson.join(' ').toLowerCase();\n const blogTypes = schemaTypes.filter(t =>\n (blogSchemaText.includes(`\"${t}\"`) || blogSchemaText.includes(`\"@type\":\"${t}\"`)) && !foundTypes.includes(t)\n );\n if (blogTypes.length > 0) {\n score += Math.min(2, blogTypes.length);\n findings.push({ severity: 'info', detail: `Additional schema types found on blog pages: ${blogTypes.join(', ')}` });\n }\n // FAQPage on blog is especially valuable\n if (!foundTypes.includes('faqpage') && /faqpage/i.test(blogSchemaText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema found on blog posts' });\n }\n }\n }\n\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 3: Q&A Content Format\nfunction checkQAFormat(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Count question headings from homepage + blog sample combined\n const combinedHtml = getCombinedHtml(data);\n const hTagContent = (combinedHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n\n if (questionHeadings.length >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${questionHeadings.length} question-format heading(s) found`, fix: 'Structure more content as Q&A with question headings (H2/H3) followed by direct answers' });\n } else {\n findings.push({ severity: 'high', detail: 'No question-format headings found', fix: 'Add Q&A sections with headings like \"What is...?\", \"How does...?\" followed by concise answers' });\n }\n\n // Check for question heading followed by a paragraph (20-500 chars, allowing nested inline tags)\n const hasDirectAnswers = /<h[2-3][^>]*>[^<]*\\?<\\/h[2-3]>\\s*<p[^>]*>[\\s\\S]{20,500}<\\/p>/i.test(combinedHtml);\n if (hasDirectAnswers) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Content uses direct-answer format after question headings' });\n } else {\n findings.push({ severity: 'medium', detail: 'Content does not follow direct-answer format', fix: 'Start each answer paragraph with a concise 1-2 sentence definition before elaborating' });\n }\n\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Proper single H1 tag hierarchy' });\n } else if (h1Count === 0) {\n findings.push({ severity: 'high', detail: 'No H1 tag found', fix: 'Add exactly one H1 tag as the main page heading' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple H1 tags found (${h1Count})`, fix: 'Use only one H1 per page; use H2/H3 for subsections' });\n }\n\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 4: Clean HTML (includes HTTPS check - v4 requirement)\nfunction checkCleanHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n const httpsAvailable = data.protocol === 'https';\n\n // HTTPS check (v4 requirement: no HTTPS = cap at 3)\n if (httpsAvailable) {\n findings.push({ severity: 'info', detail: 'Site serves over HTTPS' });\n } else {\n findings.push({ severity: 'critical', detail: 'Site does not support HTTPS', fix: 'Enable HTTPS with a valid SSL certificate. Sites without HTTPS are penalized by AI crawlers.' });\n }\n\n // Check for semantic elements\n const hasMain = /<main[\\s>]/i.test(html);\n const hasArticle = /<article[\\s>]/i.test(html);\n const hasSection = /<section[\\s>]/i.test(html);\n\n const semanticCount = [hasMain, hasArticle, hasSection].filter(Boolean).length;\n score += Math.min(3, semanticCount * 1);\n if (semanticCount >= 2) {\n findings.push({ severity: 'info', detail: `Uses semantic HTML5 elements: ${[hasMain && 'main', hasArticle && 'article', hasSection && 'section'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'medium', detail: 'Limited semantic HTML5 usage', fix: 'Wrap main content in <main>, use <article> for standalone content, <section> for grouped content' });\n }\n\n // Check H1 count\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Single H1 tag found - correct heading hierarchy' });\n } else {\n findings.push({ severity: h1Count === 0 ? 'high' : 'medium', detail: `${h1Count === 0 ? 'No' : 'Multiple'} H1 tag(s) found (${h1Count})`, fix: 'Use exactly one H1 per page' });\n }\n\n // Check for text content (not JS-only)\n const textContent = html.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n if (textContent.length > 500) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Page has substantial text content accessible without JavaScript' });\n } else {\n findings.push({ severity: 'high', detail: 'Very little text content visible in HTML source', fix: 'Ensure key content is server-rendered, not loaded via JavaScript only' });\n }\n\n // Check for meta tags\n const hasMetaDesc = /<meta[^>]*name=\"description\"[^>]*>/i.test(html);\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n if (hasMetaDesc && hasTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Page has title and meta description' });\n } else {\n findings.push({ severity: 'medium', detail: `Missing ${!hasTitle ? 'title tag' : ''}${!hasTitle && !hasMetaDesc ? ' and ' : ''}${!hasMetaDesc ? 'meta description' : ''}`, fix: 'Add <title> and <meta name=\"description\"> tags' });\n }\n\n // HTTPS cap: no HTTPS = max 3 for this criterion\n if (!httpsAvailable) {\n score = Math.min(score, 3);\n }\n\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 5: Entity Consistency\nfunction checkEntityConsistency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Check for phone numbers with context validation\n // A regex match only counts if it has supporting context (tel: link, schema, or nearby keywords)\n const hasTelLink = /href=\"tel:/i.test(html);\n const hasSchemaTelephone = /\"telephone\"/i.test(html);\n const phoneContextWords = /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i;\n\n const phoneRegex = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n const phones = text.match(phoneRegex) || [];\n const contextValidatedPhones: string[] = [];\n\n if (hasTelLink || hasSchemaTelephone) {\n // Global context validates all matches\n contextValidatedPhones.push(...phones);\n } else {\n // Check each match for nearby context words (~100 chars around match)\n let match: RegExpExecArray | null;\n const phoneRegex2 = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n while ((match = phoneRegex2.exec(text)) !== null) {\n const start = Math.max(0, match.index - 100);\n const end = Math.min(text.length, match.index + match[0].length + 100);\n const surrounding = text.slice(start, end);\n if (phoneContextWords.test(surrounding)) {\n contextValidatedPhones.push(match[0]);\n }\n }\n }\n\n const uniquePhones = [...new Set(contextValidatedPhones.map(p => p.replace(/\\D/g, '')))];\n if (uniquePhones.length === 1) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Single consistent phone number found' });\n } else if (uniquePhones.length > 1) {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple phone numbers found (${uniquePhones.length})`, fix: 'Use one primary phone number consistently across all pages' });\n } else {\n findings.push({ severity: 'low', detail: 'No phone number found on homepage' });\n score += 1;\n }\n\n // Check for address\n const hasAddress = /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text);\n if (hasAddress) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Physical address found on page' });\n }\n\n // Check for Organization schema\n const hasOrgSchema = /organization|localbusiness/i.test(html);\n if (hasOrgSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Organization/LocalBusiness schema reinforces entity identity' });\n } else {\n findings.push({ severity: 'high', detail: 'No Organization schema to reinforce entity identity', fix: 'Add Organization JSON-LD with consistent name, address, phone, and social links' });\n }\n\n // Check for social proof\n const hasSameAs = /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html);\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Social media / sameAs references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No social media links or sameAs found', fix: 'Add sameAs links in Organization schema to social profiles' });\n }\n\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 6: robots.txt\nfunction checkRobotsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.robotsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n findings.push({ severity: 'high', detail: 'No robots.txt file found', fix: 'Create a robots.txt that explicitly allows AI crawlers' });\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: 2, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text.toLowerCase();\n let score = 3; // exists\n\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => text.includes(c));\n\n if (mentionedCrawlers.length > 0) {\n score += 3;\n findings.push({ severity: 'info', detail: `AI crawlers mentioned: ${mentionedCrawlers.join(', ')}` });\n\n const blocked = mentionedCrawlers.filter(c => {\n // Extract only this crawler's section (up to next User-agent: or EOF)\n // NOTE: do NOT use 'm' flag here - it makes $ match end-of-line, causing\n // non-greedy [\\s\\S]*? to stop at the first line instead of capturing the full section\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(result.text);\n if (!match) return false;\n const section = match[1];\n // If section has \"Allow: /\", the crawler is explicitly allowed (not blocked)\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n // Check for \"Disallow: /\" (root-only, not /path) within this section\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n if (blocked.length > 0) {\n score -= 2;\n findings.push({ severity: 'critical', detail: `AI crawlers BLOCKED: ${blocked.join(', ')}`, fix: 'Change Disallow: / to Allow: / for AI crawler user-agents' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'AI crawlers are allowed to index the site' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No explicit AI crawler rules in robots.txt', fix: 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot with Allow: /' });\n }\n\n if (text.includes('sitemap:')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Sitemap URL referenced in robots.txt' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap reference in robots.txt', fix: 'Add Sitemap: https://yoursite.com/sitemap.xml to robots.txt' });\n }\n\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P0' };\n}\n\n// Criterion 7: FAQ Section\nfunction checkFAQSection(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n const homepage = data.homepage;\n const hasHomepageFAQ = homepage && /faq|frequently\\s+asked/i.test(homepage.text);\n\n if (hasHomepageFAQ) {\n score += 2;\n findings.push({ severity: 'info', detail: 'FAQ content found on homepage' });\n } else {\n findings.push({ severity: 'low', detail: 'No FAQ content found on homepage', fix: 'Add an FAQ section to your homepage addressing common visitor questions' });\n }\n\n const faqPage = data.faqPage;\n const hasFaqPage = faqPage && faqPage.status === 200 && faqPage.text.length > 500;\n\n if (hasFaqPage) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Dedicated FAQ page exists' });\n\n if (/accordion|toggle|collaps|expand/i.test(faqPage.text)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQ uses accordion/toggle UI pattern' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No dedicated FAQ page found at /faq', fix: 'Create a comprehensive FAQ page at /faq covering common questions about your service' });\n }\n\n // Check for FAQPage schema (homepage + FAQ page + blog sample)\n const blogHtml = getBlogHtml(data);\n const allHtml = (homepage?.text || '') + (faqPage?.text || '') + blogHtml;\n if (/faqpage/i.test(allHtml) && /application\\/ld\\+json/i.test(allHtml)) {\n score += 3;\n const faqOnBlog = blogHtml && /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n findings.push({ severity: 'info', detail: faqOnBlog ? 'FAQPage schema markup found on blog posts' : 'FAQPage schema markup found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema markup', fix: 'Add FAQPage JSON-LD schema to pages with FAQ content' });\n }\n\n const questionCount = (allHtml.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>/gi) || []).length;\n if (questionCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${questionCount} question headings found across checked pages` });\n } else if (questionCount >= 5) {\n findings.push({ severity: 'low', detail: `Only ${questionCount} question headings found`, fix: 'Expand FAQ to cover at least 10-15 common questions' });\n }\n\n return { criterion: 'faq_section', criterion_label: 'Comprehensive FAQ Sections', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 8: Original Data\nfunction checkOriginalData(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze homepage + all blog sample pages for original data signals\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const html = data.homepage.text;\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Stats check: strong (+3) if research context nearby, weak (+1) for generic marketing stats\n const statPatterns = /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i;\n if (statPatterns.test(allText)) {\n const researchContext = /\\b(our\\s+(?:study|analysis|research|data|survey|findings|report)|we\\s+(?:surveyed|analyzed|studied|measured|tracked)|proprietary|methodology|original\\s+research)\\b/i;\n if (researchContext.test(allText)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Proprietary statistics with research context found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Statistics found but without research context (e.g., \"500+ clients\")', fix: 'Add context about your methodology: \"Our analysis of X found...\" or \"We surveyed Y...\"' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No proprietary data or statistics found', fix: 'Add unique statistics, case study results, or industry data that LLMs would cite as authoritative' });\n }\n\n // Case studies: with nearby metric (+3), without metric (+1)\n const caseStudyPattern = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i;\n if (caseStudyPattern.test(text)) {\n // Check for a numeric metric within ~200 chars of the case study mention\n const caseStudyRegex = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/gi;\n let hasMetricNearby = false;\n let csMatch: RegExpExecArray | null;\n while ((csMatch = caseStudyRegex.exec(text)) !== null) {\n const start = Math.max(0, csMatch.index - 200);\n const end = Math.min(text.length, csMatch.index + csMatch[0].length + 200);\n const surrounding = text.slice(start, end);\n if (/\\d+%|\\$[\\d,]+|\\d+x\\b/i.test(surrounding)) {\n hasMetricNearby = true;\n break;\n }\n }\n if (hasMetricNearby) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials with specific metrics found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Case studies or testimonials mentioned but without specific metrics', fix: 'Add measurable outcomes to case studies (e.g., \"increased traffic by 45%\")' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No case studies or testimonials found', fix: 'Add case studies with specific outcomes and metrics' });\n }\n\n // Expert attribution (+2) - check homepage + blog\n const combinedText = data.blogSample && data.blogSample.length > 0\n ? text + ' ' + data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ')\n : text;\n if (/written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(combinedText)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Expert attribution or credentials found' });\n } else {\n findings.push({ severity: 'low', detail: 'No expert attribution or credentials visible', fix: 'Add author bios with credentials to establish E-E-A-T signals' });\n }\n\n // Blog check: require actual href links to content paths, not just the word \"blog\"\n const contentLinkPattern = /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i;\n if (contentLinkPattern.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Links to blog/articles section found on site' });\n } else {\n findings.push({ severity: 'medium', detail: 'No links to blog or articles section found', fix: 'Create a content section with expert articles and link to it from your homepage' });\n }\n\n // Blog sample enhancement: case studies from blog posts\n if (data.blogSample && data.blogSample.length > 0 && !caseStudyPattern.test(text)) {\n const blogText = data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n if (caseStudyPattern.test(blogText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials found on blog posts' });\n }\n }\n\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P2' };\n}\n\n// Criterion 9: Internal Linking\nfunction checkInternalLinking(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n\n if (internalLinks.length >= 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `${internalLinks.length} internal links found on homepage` });\n } else if (internalLinks.length >= 10) {\n score += 2;\n findings.push({ severity: 'low', detail: `${internalLinks.length} internal links on homepage`, fix: 'Add more internal links to key service/content pages' });\n } else {\n findings.push({ severity: 'high', detail: `Only ${internalLinks.length} internal links on homepage`, fix: 'Add prominent internal links to service pages, FAQ, blog, and about pages' });\n }\n\n if (/breadcrumb|aria-label=\"breadcrumb\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Breadcrumb navigation detected' });\n } else {\n findings.push({ severity: 'medium', detail: 'No breadcrumb navigation found', fix: 'Add breadcrumb navigation with BreadcrumbList schema markup' });\n }\n\n if (/<nav[\\s>]/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Semantic <nav> element used for navigation' });\n } else {\n findings.push({ severity: 'low', detail: 'No semantic <nav> element found', fix: 'Wrap navigation menus in <nav> for better AI and accessibility parsing' });\n }\n\n if (/related|see\\s+also|learn\\s+more|explore|you\\s+may\\s+also/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Related content or cross-linking sections found' });\n } else {\n findings.push({ severity: 'low', detail: 'No related content or cross-linking found', fix: 'Add \"Related Services\" or \"Learn More\" sections to build topic clusters' });\n }\n\n if (/<footer[\\s>]/i.test(html)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Footer element with likely navigation links' });\n } else {\n findings.push({ severity: 'low', detail: 'No <footer> element found', fix: 'Add a <footer> with navigation links, contact info, and site structure' });\n }\n\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 10: Semantic HTML\nfunction checkSemanticHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for semantic element detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n let score = 0;\n\n const checks: [string, RegExp, string][] = [\n ['<main>', /<main[\\s>]/i, 'Wrap primary page content in <main>'],\n ['<article>', /<article[\\s>]/i, 'Use <article> for standalone content blocks'],\n ['<time>', /<time[\\s>]/i, 'Use <time> elements for dates'],\n ['<nav>', /<nav[\\s>]/i, 'Use <nav> for navigation sections'],\n ['<header>', /<header[\\s>]/i, 'Use <header> for page/section headers'],\n ['<footer>', /<footer[\\s>]/i, 'Use <footer> for page/section footers'],\n ];\n\n let found = 0;\n for (const [name, regex, fix] of checks) {\n if (regex.test(combinedHtml)) {\n found++;\n } else {\n findings.push({ severity: 'low', detail: `Missing ${name} element`, fix });\n }\n }\n score += Math.min(4, Math.floor(found * 0.7));\n if (found >= 4) findings.push({ severity: 'info', detail: `${found}/6 key semantic HTML5 elements found` });\n\n // Check img alt text\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n if (images.length > 0) {\n const ratio = imagesWithAlt.length / images.length;\n if (ratio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(ratio * 100)}% of images have alt text` });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${Math.round(ratio * 100)}% of images have alt text`, fix: 'Add descriptive alt text to all images' });\n }\n }\n\n // Check lang attribute\n if (/lang=\"[a-z]{2}\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'HTML lang attribute set' });\n } else {\n findings.push({ severity: 'medium', detail: 'Missing lang attribute on <html> tag', fix: 'Add lang=\"en\" (or appropriate language) to the <html> tag' });\n }\n\n // Check for ARIA roles\n if (/role=\"|aria-/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'ARIA attributes found for accessibility' });\n } else {\n findings.push({ severity: 'low', detail: 'No ARIA roles or attributes found', fix: 'Add ARIA roles and labels to improve accessibility and semantic parsing' });\n }\n\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 11: Content Freshness\nfunction checkContentFreshness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // JSON-LD date signals (datePublished, dateModified)\n const hasDatePublished = /datePublished|dateCreated/i.test(html);\n const hasDateModified = /dateModified/i.test(html);\n if (hasDatePublished || hasDateModified) {\n score += 3;\n findings.push({ severity: 'info', detail: `JSON-LD date properties found: ${[hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'high', detail: 'No JSON-LD date properties (datePublished/dateModified) found', fix: 'Add datePublished and dateModified to Article or WebPage schema' });\n }\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${timeElements.length} <time> elements found` });\n } else if (timeElements.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 <time> element found', fix: 'Use <time datetime=\"...\"> for all dates to help AI parsers' });\n } else {\n findings.push({ severity: 'medium', detail: 'No <time> elements found', fix: 'Wrap publication and modification dates in <time datetime=\"...\"> elements' });\n }\n\n // Article meta (article:published_time, article:modified_time)\n const hasArticleMeta = /article:published_time|article:modified_time/i.test(html);\n if (hasArticleMeta) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Open Graph article date meta tags found' });\n } else {\n findings.push({ severity: 'low', detail: 'No article:published_time or article:modified_time meta tags', fix: 'Add Open Graph article date meta tags' });\n }\n\n // Recency check - look for recent year references\n const currentYear = new Date().getFullYear();\n const hasRecentYear = html.includes(String(currentYear)) || html.includes(String(currentYear - 1));\n if (hasRecentYear) {\n score += 2;\n findings.push({ severity: 'info', detail: `References to ${currentYear} or ${currentYear - 1} found, suggesting recent content` });\n } else {\n findings.push({ severity: 'low', detail: 'No references to recent years found on homepage' });\n }\n\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 12: Sitemap Completeness\nfunction checkSitemapCompleteness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'critical', detail: 'No sitemap.xml found', fix: 'Create a sitemap.xml with all indexable pages and submit to search engines' });\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const text = sitemap.text;\n let score = 2; // exists\n\n // Valid XML structure\n if (text.includes('<urlset') || text.includes('<sitemapindex')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Valid sitemap XML structure detected' });\n } else {\n findings.push({ severity: 'high', detail: 'sitemap.xml does not contain valid XML structure', fix: 'Ensure sitemap uses proper <urlset> or <sitemapindex> XML format' });\n }\n\n // Count URLs\n const urlCount = (text.match(/<loc>/gi) || []).length;\n if (urlCount >= 50) {\n score += 3;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount >= 10) {\n score += 2;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${urlCount} URL(s) in sitemap`, fix: 'Add all important pages to your sitemap' });\n }\n\n // Recent lastmod dates\n const lastmodMatches = text.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length > 0) {\n score += 2;\n findings.push({ severity: 'info', detail: `${lastmodMatches.length} URLs have lastmod dates` });\n } else {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add <lastmod> dates to sitemap entries for freshness signals' });\n }\n\n // Sitemap index\n if (text.includes('<sitemapindex')) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Sitemap index found, indicating organized sitemap structure' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap index structure', fix: 'Use a <sitemapindex> with multiple child sitemaps for larger sites to improve crawl efficiency' });\n }\n\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 13: RSS Feed\nfunction checkRssFeed(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // Check for RSS link tag in homepage\n const hasRssLink = data.homepage && /<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"/i.test(data.homepage.text);\n if (hasRssLink) {\n score += 3;\n findings.push({ severity: 'info', detail: 'RSS/Atom feed link tag found in homepage <head>' });\n } else {\n findings.push({ severity: 'high', detail: 'No RSS/Atom feed link tag in homepage', fix: 'Add <link rel=\"alternate\" type=\"application/rss+xml\" href=\"/feed\"> to your <head>' });\n }\n\n // Check for valid feed content\n const feed = data.rssFeed;\n if (feed && feed.status === 200) {\n const feedText = feed.text;\n const isValidFeed = feedText.includes('<rss') || feedText.includes('<feed') || feedText.includes('<channel');\n if (isValidFeed) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Valid RSS/Atom feed content detected' });\n\n // Count items\n const itemCount = (feedText.match(/<item[\\s>]|<entry[\\s>]/gi) || []).length;\n if (itemCount >= 5) {\n score += 4;\n findings.push({ severity: 'info', detail: `Feed contains ${itemCount} items` });\n } else if (itemCount > 0) {\n score += 2;\n findings.push({ severity: 'low', detail: `Feed contains only ${itemCount} item(s)`, fix: 'Publish more content to populate your RSS feed with at least 5 items' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'Feed URL returned content but not valid RSS/Atom XML', fix: 'Ensure your feed outputs valid RSS 2.0 or Atom XML' });\n }\n } else if (!hasRssLink) {\n findings.push({ severity: 'medium', detail: 'No accessible RSS/Atom feed found', fix: 'Create an RSS feed to help AI engines discover and index new content automatically' });\n }\n\n return { criterion: 'rss_feed', criterion_label: 'RSS/Atom Feed', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 14: Table & List Extractability\nfunction checkTableListExtractability(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML so blog tables/lists count\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Tables with headers\n const tables = html.match(/<table[\\s>]/gi) || [];\n const tablesWithHeaders = (html.match(/<table[\\s\\S]*?<\\/table>/gi) || []).filter(t => /<th[\\s>]/i.test(t));\n if (tablesWithHeaders.length >= 1) {\n score += 3;\n findings.push({ severity: 'info', detail: `${tablesWithHeaders.length} table(s) with <th> headers found` });\n } else if (tables.length > 0) {\n score += 1;\n findings.push({ severity: 'medium', detail: `${tables.length} table(s) found but without <th> header cells`, fix: 'Add <th> header cells to tables for better AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No HTML tables found', fix: 'Use comparison tables with <th> headers for structured data AI engines can extract' });\n }\n if (tablesWithHeaders.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Multiple well-structured tables present' });\n } else if (tablesWithHeaders.length === 1) {\n findings.push({ severity: 'low', detail: 'Only 1 table with headers found', fix: 'Add more comparison or data tables with <th> headers to increase extractable structured content' });\n }\n\n // Ordered lists\n const olCount = (html.match(/<ol[\\s>]/gi) || []).length;\n if (olCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${olCount} ordered list(s) found - good for step-by-step content` });\n } else {\n findings.push({ severity: 'low', detail: 'No ordered lists (<ol>) found', fix: 'Use <ol> for sequential content (steps, rankings, processes)' });\n }\n\n // Unordered lists\n const ulCount = (html.match(/<ul[\\s>]/gi) || []).length;\n if (ulCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${ulCount} unordered list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No unordered lists (<ul>) found', fix: 'Use <ul> for feature lists, benefits, and bullet-point content' });\n }\n\n // List items count\n const liCount = (html.match(/<li[\\s>]/gi) || []).length;\n if (liCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${liCount} list items - substantial extractable content` });\n }\n\n // Definition lists\n const dlCount = (html.match(/<dl[\\s>]/gi) || []).length;\n if (dlCount >= 1) {\n score += 1;\n findings.push({ severity: 'info', detail: `${dlCount} definition list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No definition lists (<dl>) found', fix: 'Use <dl>/<dt>/<dd> for term-definition pairs to improve AI extractability' });\n }\n\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 15: Definition Patterns\nfunction checkDefinitionPatterns(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for definition pattern detection\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const html = combinedHtml;\n let score = 0;\n\n // \"X is\" / \"X are\" / \"X refers to\" / \"defined as\" patterns\n const definitionPatterns = [\n /\\b\\w[\\w\\s]{2,30}\\bis\\s+(?:a|an|the)\\s/gi,\n /\\b\\w[\\w\\s]{2,30}\\bare\\s+(?:a|an|the)\\s/gi,\n /\\brefers?\\s+to\\b/gi,\n /\\bdefined\\s+as\\b/gi,\n /\\bknown\\s+as\\b/gi,\n /\\bmeans?\\s+that\\b/gi,\n ];\n\n let patternCount = 0;\n for (const pattern of definitionPatterns) {\n const matches = text.match(pattern) || [];\n patternCount += matches.length;\n }\n\n if (patternCount >= 3) {\n score += 5;\n findings.push({ severity: 'info', detail: `${patternCount} definition-style patterns found (e.g., \"X is a...\", \"refers to\", \"defined as\")` });\n } else if (patternCount >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `Only ${patternCount} definition pattern(s) found`, fix: 'Start key descriptions with clear definition patterns like \"X is a...\" or \"X refers to...\"' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition patterns found', fix: 'Add clear definitions using patterns like \"[Term] is [definition]\" that AI engines can extract as snippets' });\n }\n\n // Early placement (in first 2000 chars of text content)\n const earlyText = text.slice(0, 2000);\n const earlyDefinitions = definitionPatterns.some(p => p.test(earlyText));\n // Reset lastIndex since we used global flags\n definitionPatterns.forEach(p => { p.lastIndex = 0; });\n if (earlyDefinitions) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Definition patterns appear early in page content - good for snippet extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No definition patterns in the first 2000 characters of content', fix: 'Place key definitions early on the page where AI engines prioritize extraction' });\n }\n\n // <dfn> or <abbr> usage\n const hasDfn = /<dfn[\\s>]/i.test(html);\n const hasAbbr = /<abbr[\\s>]/i.test(html);\n if (hasDfn || hasAbbr) {\n score += 1;\n findings.push({ severity: 'info', detail: `Semantic definition elements found: ${[hasDfn && '<dfn>', hasAbbr && '<abbr>'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'low', detail: 'No <dfn> or <abbr> elements found', fix: 'Use <dfn> for term definitions and <abbr> for abbreviations to help AI parse terminology' });\n }\n\n // Glossary or definition-list patterns\n if (/<dl[\\s>]/i.test(html) || /glossary|definitions|terminology/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Glossary or definition list structure detected' });\n } else {\n findings.push({ severity: 'low', detail: 'No glossary or definition list found', fix: 'Add a glossary section using <dl>/<dt>/<dd> for key industry terms' });\n }\n\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 16: Direct Answer Density\nfunction checkDirectAnswerDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n // Use combined HTML for Q&A pair and paragraph detection\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Q&A pairs: question heading followed by paragraph\n const qaPairs = html.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>\\s*<p[^>]*>/gi) || [];\n if (qaPairs.length >= 3) {\n score += 6;\n findings.push({ severity: 'info', detail: `${qaPairs.length} question-answer pairs found (question heading + direct answer paragraph)` });\n } else if (qaPairs.length >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `${qaPairs.length} question-answer pair(s) found`, fix: 'Add more question headings (H2/H3) immediately followed by concise answer paragraphs' });\n } else {\n findings.push({ severity: 'high', detail: 'No direct question-answer pairs found', fix: 'Structure content with question headings (e.g., \"What is X?\") immediately followed by a concise answer paragraph' });\n }\n\n // Snippet-zone paragraphs (40-150 words - ideal for AI extraction)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n const snippetZoneParagraphs = paragraphs.filter(p => {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = text.split(/\\s+/).length;\n return wordCount >= 40 && wordCount <= 150;\n });\n if (snippetZoneParagraphs.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${snippetZoneParagraphs.length} paragraphs in snippet zone (40-150 words) - ideal for AI extraction` });\n } else if (snippetZoneParagraphs.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${snippetZoneParagraphs.length} paragraph(s) in optimal snippet length`, fix: 'Write more paragraphs in the 40-150 word range for AI snippet extraction' });\n } else {\n findings.push({ severity: 'medium', detail: 'No paragraphs in the optimal snippet zone (40-150 words)', fix: 'Write self-contained paragraphs of 40-150 words that directly answer common questions' });\n }\n\n // Direct answer openers\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const directOpeners = /\\b(yes|no|the answer is|in short|simply put|to summarize)\\b/gi;\n const openerCount = (text.match(directOpeners) || []).length;\n if (openerCount >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Direct answer openers found (e.g., \"Yes,\", \"In short,\")' });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no direct answer openers found', fix: 'Start answers with direct phrases like \"Yes,\", \"No,\", \"In short,\" to signal definitive answers to AI engines' });\n }\n\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 17: Content Licensing\nfunction checkContentLicensing(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // ai.txt exists (ignore HTML catch-all responses)\n const aiTxt = data.aiTxt;\n if (aiTxt && aiTxt.status === 200 && aiTxt.text.length > 20 && !isHtmlResponse(aiTxt)) {\n score += 4;\n findings.push({ severity: 'info', detail: `ai.txt file found (${aiTxt.text.length} characters)` });\n } else {\n findings.push({ severity: 'high', detail: 'No ai.txt file found', fix: 'Create /ai.txt to declare your AI usage policy and content permissions for AI crawlers' });\n }\n\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Policy language on page\n const hasPolicyLanguage = /content\\s+policy|terms\\s+of\\s+use|usage\\s+rights|permission|copyright\\s+policy|licensing|creative\\s+commons/i.test(text);\n if (hasPolicyLanguage) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Content policy or licensing language found on page' });\n } else {\n findings.push({ severity: 'low', detail: 'No content policy or licensing language visible', fix: 'Add clear content usage terms or licensing information' });\n }\n\n // Schema license property\n const hasLicenseSchema = /license|copyrightHolder|copyrightYear/i.test(html) && /application\\/ld\\+json/i.test(html);\n if (hasLicenseSchema) {\n score += 2;\n findings.push({ severity: 'info', detail: 'License or copyright properties found in schema markup' });\n } else {\n findings.push({ severity: 'low', detail: 'No license or copyright properties in schema', fix: 'Add license, copyrightHolder, and copyrightYear to your schema markup' });\n }\n\n // TDM (Text and Data Mining) or Creative Commons\n const hasTdmOrCC = /tdm|text\\s+and\\s+data\\s+mining|creative\\s+commons|CC\\s+BY|creativecommons\\.org/i.test(html + (aiTxt?.text || ''));\n if (hasTdmOrCC) {\n score += 2;\n findings.push({ severity: 'info', detail: 'TDM or Creative Commons licensing references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No TDM or Creative Commons licensing references found', fix: 'Add Text and Data Mining (TDM) permissions or Creative Commons licensing to signal AI-friendly content use' });\n }\n\n return { criterion: 'content_licensing', criterion_label: 'Content Licensing & AI Permissions', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 18: Author Schema Depth\nfunction checkAuthorSchemaDepth(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for author detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Person schema (check combined - blog posts often have author schema)\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(combinedHtml);\n if (hasPersonSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Person schema found in JSON-LD' });\n } else {\n findings.push({ severity: 'medium', detail: 'No Person schema found', fix: 'Add Person schema for authors with name, jobTitle, knowsAbout, and sameAs properties' });\n }\n\n // jobTitle or knowsAbout properties\n const hasJobTitle = /jobTitle|knowsAbout|expertise|hasCredential/i.test(combinedHtml);\n if (hasJobTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author credential properties found (jobTitle/knowsAbout)' });\n } else {\n findings.push({ severity: 'low', detail: 'No jobTitle or knowsAbout in author schema', fix: 'Add jobTitle and knowsAbout to Person schema to establish expertise' });\n }\n\n // sameAs links (social profiles)\n const hasSameAs = /sameAs/i.test(combinedHtml) && hasPersonSchema;\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author sameAs social profile links found' });\n } else {\n findings.push({ severity: 'low', detail: 'No sameAs links to author social profiles', fix: 'Add sameAs URLs (LinkedIn, GitHub) to Person schema to strengthen entity connections' });\n }\n\n // Visible byline\n const hasByline = /written\\s+by|authored?\\s+by|by\\s+[A-Z][a-z]+\\s+[A-Z]/i.test(text) ||\n /class=\"[^\"]*author[^\"]*\"/i.test(html) ||\n /rel=\"author\"/i.test(html);\n if (hasByline) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Visible author byline or attribution found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No visible author byline found', fix: 'Add visible author names with credentials to establish E-E-A-T' });\n }\n\n // <address> element for contact\n if (/<address[\\s>]/i.test(combinedHtml)) {\n score += 1;\n findings.push({ severity: 'info', detail: '<address> element found for contact information' });\n } else {\n findings.push({ severity: 'low', detail: 'No <address> element found for contact information', fix: 'Add an <address> element with contact details to reinforce entity identity' });\n }\n\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 19: Fact Density\nfunction checkFactDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze across homepage + all blog sample pages for fact density\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = allText;\n const pageCount = allPages.length;\n let score = 0;\n\n // Numeric data points (percentages, money, counts)\n const dataPoints = text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD|\\s*EUR)/g) || [];\n const countPhrases = text.match(/\\d+(?:,\\d{3})*\\+?\\s+(?:users?|clients?|customers?|companies|businesses|patients?|members?|employees?|projects?|downloads?)/gi) || [];\n const totalDataPoints = dataPoints.length + countPhrases.length;\n const avgPerPage = pageCount > 0 ? totalDataPoints / pageCount : 0;\n\n if (avgPerPage >= 4) {\n score += 5;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages (avg ${avgPerPage.toFixed(1)}/page)` });\n } else if (avgPerPage >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages` });\n } else if (totalDataPoints >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${totalDataPoints} quantitative data point(s) found across ${pageCount} pages`, fix: 'Add more specific numbers, percentages, and metrics to strengthen credibility' });\n } else {\n findings.push({ severity: 'high', detail: 'No quantitative data points found', fix: 'Add specific statistics (percentages, counts, comparisons) that AI engines can cite' });\n }\n\n // Year references (suggest sourced/dated claims)\n const yearRefs = text.match(/(?:19|20)\\d{2}/g) || [];\n const uniqueYears = [...new Set(yearRefs)];\n if (uniqueYears.length >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueYears.length} different year references found - suggests dated, verifiable claims` });\n } else if (uniqueYears.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 year reference found on page', fix: 'Add more dated references and timestamps to demonstrate current, verifiable information' });\n } else {\n findings.push({ severity: 'low', detail: 'No year references found on page', fix: 'Include specific years and dates to provide verifiable, time-anchored facts' });\n }\n\n // Attribution phrases (source citations)\n const attributions = text.match(/according\\s+to|source:|study\\s+(?:by|from)|research\\s+(?:by|from|shows)|data\\s+from|report\\s+(?:by|from)|published\\s+(?:by|in)/gi) || [];\n if (attributions.length >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${attributions.length} source attribution(s) found (e.g., \"according to\", \"study by\")` });\n } else {\n findings.push({ severity: 'low', detail: 'No source attributions found', fix: 'Add citations like \"According to [source]\" or \"Research from [org] shows\" for credibility' });\n }\n\n // Units of measurement\n const units = text.match(/\\d+\\s*(?:hours?|minutes?|days?|weeks?|months?|years?|miles?|km|lbs?|kg|mg|sq\\s*ft|acres?|gallons?|liters?)/gi) || [];\n if (units.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: `${units.length} measurement units found (hours, miles, etc.) - adds factual precision` });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no units of measurement found', fix: 'Include specific measurements (hours, miles, sq ft, etc.) to add factual precision AI engines can extract' });\n }\n\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 20: Canonical URL\nfunction checkCanonicalUrl(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Canonical link present (handle either attribute order: rel before href OR href before rel)\n const canonicalMatch = html.match(/<link[^>]*rel=\"canonical\"[^>]*href=\"([^\"]*)\"[^>]*>/i)\n || html.match(/<link[^>]*href=\"([^\"]*)\"[^>]*rel=\"canonical\"[^>]*>/i);\n if (canonicalMatch) {\n score += 4;\n findings.push({ severity: 'info', detail: `Canonical URL found: ${canonicalMatch[1].slice(0, 80)}` });\n\n // Self-referencing (canonical points to the same domain)\n const canonicalUrl = canonicalMatch[1];\n if (canonicalUrl.includes(data.domain)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Canonical URL is self-referencing (points to same domain)' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL points to a different domain', fix: 'Ensure canonical URL points to the authoritative version of this page' });\n }\n\n // HTTPS canonical\n if (canonicalUrl.startsWith('https://')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Canonical URL uses HTTPS' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL does not use HTTPS', fix: 'Update canonical URL to use https://' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No canonical URL tag found', fix: 'Add <link rel=\"canonical\" href=\"https://yoursite.com/page\"> to prevent duplicate content issues' });\n }\n\n // Check for duplicate canonical tags (match either attribute order)\n const allCanonicals = html.match(/<link[^>]*(?:rel=\"canonical\"|rel='canonical')[^>]*>/gi) || [];\n if (allCanonicals.length > 1) {\n score -= 1;\n findings.push({ severity: 'high', detail: `${allCanonicals.length} canonical tags found - must have exactly one`, fix: 'Remove duplicate canonical tags, keeping only one per page' });\n } else if (allCanonicals.length === 1) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Single canonical tag present (no duplicates)' });\n }\n\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: Math.max(0, Math.min(10, score)), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Helper: analyze sitemap lastmod dates for content velocity ──────────────\n\nexport interface SitemapDateAnalysis {\n recentCount: number;\n isUniform: boolean;\n uniformDetail?: string;\n totalWithDates: number;\n distinctRecentDays: number;\n}\n\nexport function countRecentSitemapDates(sitemapText: string): SitemapDateAnalysis {\n const lastmodMatches = sitemapText.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length === 0) {\n return { recentCount: 0, isUniform: false, totalWithDates: 0, distinctRecentDays: 0 };\n }\n\n const now = new Date();\n const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n\n // Parse all dates and group by day\n const dayCounts: Record<string, number> = {};\n let recentCount = 0;\n const recentDays = new Set<string>();\n\n for (const match of lastmodMatches) {\n const dateStr = match.replace(/<\\/?lastmod>/gi, '').trim();\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) continue;\n\n const dayKey = date.toISOString().split('T')[0];\n dayCounts[dayKey] = (dayCounts[dayKey] || 0) + 1;\n\n if (date >= ninetyDaysAgo) {\n recentCount++;\n recentDays.add(dayKey);\n }\n }\n\n // Detect uniform pattern: if most common day has >80% of all dated URLs\n const totalWithDates = Object.values(dayCounts).reduce((a, b) => a + b, 0);\n const maxDayCount = Math.max(...Object.values(dayCounts));\n const isUniform = totalWithDates >= 5 && maxDayCount / totalWithDates > 0.8;\n\n let uniformDetail: string | undefined;\n if (isUniform) {\n const topDay = Object.entries(dayCounts).find(([, count]) => count === maxDayCount)![0];\n uniformDetail = `${maxDayCount} of ${totalWithDates} URLs share lastmod date ${topDay} - likely auto-generated by build system`;\n }\n\n return {\n recentCount,\n isUniform,\n uniformDetail,\n totalWithDates,\n distinctRecentDays: recentDays.size,\n };\n}\n\n// ─── Blog URL extraction from sitemap ────────────────────────────────────────\n\nconst BLOG_PATH_PATTERNS = /\\/(?:[^/]*-?)?(?:blog|articles?|insights?|guides?|resources?|news|posts?|learn|help|how-?to|tutorials?|case-stud|whitepapers?)\\b/i;\n\nconst EXCLUDE_PATH_PATTERNS = /\\/(?:tag|category|author|page|feed|wp-content|wp-admin|wp-json|cart|checkout|login|search|api|static|assets|_next)\\b/i;\n\n/**\n * Extract blog/content URLs from a sitemap XML string.\n * Includes URLs matching common blog path patterns, plus deep paths (2+ segments).\n * Sorts by lastmod descending (newest first), returns top N.\n */\nexport function extractBlogUrlsFromSitemap(sitemapText: string, domain: string, limit: number = 50): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const candidates: { url: string; lastmod: string }[] = [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n // Must be same domain\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n // Skip root URL\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n\n // Exclude unwanted paths\n if (EXCLUDE_PATH_PATTERNS.test(path)) continue;\n\n // Include blog-like paths OR deep paths (2+ segments)\n const segments = path.split('/').filter(Boolean);\n const isBlogPath = BLOG_PATH_PATTERNS.test(path);\n const isDeepPath = segments.length >= 2;\n\n if (!isBlogPath && !isDeepPath) continue;\n } catch {\n continue;\n }\n\n const lastmodMatch = block.match(/<lastmod>([^<]+)<\\/lastmod>/i);\n const lastmod = lastmodMatch ? lastmodMatch[1].trim() : '';\n\n candidates.push({ url, lastmod });\n }\n\n // Sort by lastmod descending (newest first), URLs without lastmod go last\n candidates.sort((a, b) => {\n if (a.lastmod && b.lastmod) return b.lastmod.localeCompare(a.lastmod);\n if (a.lastmod) return -1;\n if (b.lastmod) return 1;\n return 0;\n });\n\n return candidates.slice(0, limit).map(c => c.url);\n}\n\n/**\n * Extract the best sub-sitemap URL from a sitemapindex.\n * WordPress often uses sitemapindex pointing to post-sitemap.xml, page-sitemap.xml, etc.\n * Returns null if not a sitemapindex.\n */\n/**\n * Extract ALL sub-sitemap URLs from a sitemapindex, up to a limit.\n * Used to fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries.\n */\nexport function extractAllSubSitemapUrls(sitemapText: string, limit: number = 5): string[] {\n if (!sitemapText.includes('<sitemapindex')) return [];\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(Boolean);\n\n // Prefer post/blog/article sub-sitemaps first\n const preferred = urls.filter(u => /post|blog|article|page/i.test(u));\n const rest = urls.filter(u => !preferred.includes(u));\n return [...preferred, ...rest].slice(0, limit);\n}\n\nexport function extractSubSitemapUrl(sitemapText: string): string | null {\n if (!sitemapText.includes('<sitemapindex')) return null;\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n if (sitemapLocs.length === 0) return null;\n\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(Boolean);\n\n // Prefer post/blog/article sub-sitemap\n const preferred = urls.find(u => /post|blog|article/i.test(u));\n return preferred || urls[0] || null;\n}\n\n// Criterion 21: Content Velocity\nfunction checkContentVelocity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n let score = 0;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'medium', detail: 'No sitemap available to assess content velocity', fix: 'Create a sitemap.xml with lastmod dates to signal content publishing frequency' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const analysis = countRecentSitemapDates(sitemap.text);\n\n if (analysis.totalWithDates === 0) {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add lastmod dates to sitemap entries to signal content freshness' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 2, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n score += 2;\n findings.push({ severity: 'info', detail: `${analysis.totalWithDates} pages have lastmod dates` });\n\n // Use distinct recent days when lastmod dates are uniform (build-system artifact)\n const effectiveCount = analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n\n if (analysis.isUniform) {\n findings.push({ severity: 'medium', detail: analysis.uniformDetail!, fix: 'Set genuine lastmod dates per page reflecting actual content changes, not build timestamps' });\n }\n\n if (effectiveCount >= 20) {\n score += 8;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - excellent content velocity` });\n } else if (effectiveCount >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - good velocity` });\n } else if (effectiveCount >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days` });\n } else if (effectiveCount >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${effectiveCount} ${analysis.isUniform ? 'distinct date(s)' : 'page(s) updated'} in last 90 days`, fix: 'Publish or update content more frequently to signal active maintenance' });\n } else {\n findings.push({ severity: 'medium', detail: 'No pages updated in the last 90 days', fix: 'Update existing content and publish new pages regularly' });\n }\n\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 22: Schema Coverage (depth across types)\nfunction checkSchemaCoverage(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for schema coverage (blog posts add Article, Person, FAQPage etc.)\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess schema coverage', fix: 'Add JSON-LD schema markup to improve AI engine understanding' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Count distinct schema properties\n const propertyMatches = allSchemaText.match(/\"[a-zA-Z@]+\"\\s*:/g) || [];\n const uniqueProperties = new Set(propertyMatches.map(p => p.replace(/[\":\\s]/g, '').toLowerCase()));\n\n if (uniqueProperties.size >= 15) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties used - rich schema depth` });\n } else if (uniqueProperties.size >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties found` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${uniqueProperties.size} schema properties`, fix: 'Add more properties to your schema types for richer AI understanding' });\n }\n\n // Organization depth (name, url, logo, contactPoint, sameAs, address, etc.)\n const orgProps = ['name', 'url', 'logo', 'contactpoint', 'sameas', 'address', 'telephone', 'description', 'founder', 'foundingdate'];\n const orgPropsFound = orgProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (orgPropsFound.length >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Organization schema has ${orgPropsFound.length}/10 key properties` });\n } else if (orgPropsFound.length >= 3) {\n score += 1;\n findings.push({ severity: 'low', detail: `Organization schema has only ${orgPropsFound.length}/10 key properties`, fix: 'Add more Organization properties: logo, contactPoint, sameAs, address' });\n } else {\n findings.push({ severity: 'medium', detail: `Organization schema has only ${orgPropsFound.length} key properties`, fix: 'Add essential Organization properties: name, url, logo, contactPoint, sameAs, address, telephone' });\n }\n\n // Article schema depth\n const articleProps = ['headline', 'datepublished', 'datemodified', 'author', 'image', 'description', 'publisher'];\n const articlePropsFound = articleProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (articlePropsFound.length >= 4) {\n score += 2;\n findings.push({ severity: 'info', detail: `Article schema has ${articlePropsFound.length}/7 key properties` });\n } else if (articlePropsFound.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Article schema has only ${articlePropsFound.length}/7 key properties`, fix: 'Add headline, datePublished, dateModified, author, image, and publisher to Article schema' });\n } else {\n findings.push({ severity: 'medium', detail: 'Article schema missing or has fewer than 2 key properties', fix: 'Add Article schema with headline, datePublished, author, and publisher properties' });\n }\n\n // @id linking (connected schema graph)\n const hasIdLinking = /\"@id\"\\s*:/i.test(allSchemaText);\n if (hasIdLinking) {\n score += 2;\n findings.push({ severity: 'info', detail: '@id linking found - schema types are connected in a graph' });\n } else {\n findings.push({ severity: 'low', detail: 'No @id linking between schema types', fix: 'Use @id references to connect schema types (e.g., article.publisher -> organization)' });\n }\n\n // Number of distinct types\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person', 'event', 'offer', 'review', 'aboutpage'];\n const foundTypes = schemaTypes.filter(t => allSchemaLower.includes(`\"${t}\"`));\n if (foundTypes.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${foundTypes.length} distinct schema types used: ${foundTypes.join(', ')}` });\n } else if (foundTypes.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${foundTypes.length} distinct schema types used`, fix: 'Add more schema types (FAQPage, BreadcrumbList, Service) for comprehensive AI understanding' });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${foundTypes.length} schema type(s) found - limited coverage`, fix: 'Add multiple schema types (Organization, WebSite, FAQPage, BreadcrumbList) for comprehensive AI understanding' });\n }\n\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 23: Speakable Schema (voice assistant readiness)\nfunction checkSpeakableSchema(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess speakable schema', fix: 'Add JSON-LD schema markup with SpeakableSpecification to indicate voice-readable content sections' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Detect SpeakableSpecification type or \"speakable\" property\n const hasSpeakableType = /speakablespecification/i.test(allSchemaLower);\n const hasSpeakableProperty = /\"speakable\"\\s*:/i.test(allSchemaText);\n const hasSpeakable = hasSpeakableType || hasSpeakableProperty;\n\n if (!hasSpeakable) {\n findings.push({ severity: 'medium', detail: 'No SpeakableSpecification schema found - voice assistants cannot identify readable sections', fix: 'Add SpeakableSpecification schema with cssSelector or xpath targeting key content sections (headlines, summaries, FAQ answers)' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n // Base: speakable detected\n score += 4;\n findings.push({ severity: 'info', detail: 'SpeakableSpecification schema detected - voice assistants can identify readable content' });\n\n // Check for cssSelector or xpath targeting\n const hasCssSelector = /\"cssselector\"/i.test(allSchemaLower);\n const hasXpath = /\"xpath\"/i.test(allSchemaLower);\n if (hasCssSelector || hasXpath) {\n score += 3;\n const targetType = hasCssSelector && hasXpath ? 'cssSelector and xpath' : hasCssSelector ? 'cssSelector' : 'xpath';\n findings.push({ severity: 'info', detail: `Speakable uses ${targetType} targeting for precise content selection` });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema lacks cssSelector or xpath targeting', fix: 'Add cssSelector (e.g., \".article-headline, .article-summary\") or xpath to precisely target speakable sections' });\n }\n\n // Check blog sample coverage\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogHasSpeakable = /speakablespecification/i.test(blogHtml) || /\"speakable\"\\s*:/i.test(blogHtml);\n if (blogHasSpeakable) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Speakable schema also found in blog/content pages - comprehensive voice coverage' });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema only on homepage, not found in blog/content pages', fix: 'Add SpeakableSpecification to article pages to make blog content voice-assistant readable' });\n }\n } else {\n // No blog samples - can't check, give partial credit\n findings.push({ severity: 'info', detail: 'No blog pages sampled - blog speakable coverage not assessed' });\n }\n\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 24: Query-Answer Alignment ────────────────────────────────────\n\n/** Extract question-format headings from HTML */\nfunction extractQuestionHeadings(html: string): string[] {\n const hTags = (html.match(/<h[23][^>]*>([\\s\\S]*?)<\\/h[23]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, '').trim());\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n}\n\nfunction checkQueryAnswerAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for query-answer alignment analysis' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const questionHeadings = extractQuestionHeadings(combinedHtml);\n\n if (questionHeadings.length === 0) {\n findings.push({ severity: 'info', detail: 'No question-format headings (H2/H3) found - scoring neutral', fix: 'Add question-based headings like \"What is...?\", \"How does...?\" to enable Q&A snippet extraction' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n // Split HTML on heading boundaries and check if answer follows each question heading\n let answered = 0;\n for (const qHeading of questionHeadings) {\n try {\n const escapedHeading = qHeading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(escapedHeading + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const match = pattern.exec(combinedHtml);\n if (match) {\n const afterContent = match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n // Check if there's a meaningful answer (at least 20 chars of text content)\n if (afterContent.length >= 20) {\n answered++;\n }\n }\n } catch {\n // Skip headings that produce invalid regex (e.g. from residual script content)\n }\n }\n\n const rate = Math.round((answered / questionHeadings.length) * 100);\n let score: number;\n if (rate >= 80) {\n score = 10;\n findings.push({ severity: 'info', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) followed by direct answers - excellent alignment` });\n } else if (rate >= 50) {\n score = 7;\n findings.push({ severity: 'low', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) have answers`, fix: 'Add concise answer paragraphs after remaining unanswered question headings' });\n } else if (rate > 0) {\n score = 4;\n findings.push({ severity: 'medium', detail: `Only ${answered}/${questionHeadings.length} question headings (${rate}%) are followed by answers`, fix: 'Ensure each question heading is immediately followed by a direct answer paragraph' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${questionHeadings.length} question headings found but none have direct answers`, fix: 'Add answer paragraphs (2-3 sentences) immediately after each question heading' });\n }\n\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 25: Content Cannibalization ───────────────────────────────────\n\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',\n 'of', 'with', 'by', 'from', 'is', 'it', 'as', 'be', 'was', 'are',\n 'this', 'that', 'your', 'our', 'we', 'you', 'how', 'what', 'why',\n]);\n\nfunction extractPageTitle(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const title = titleMatch?.[1]?.trim() || '';\n const h1 = h1Match?.[1]?.replace(/<[^>]*>/g, '').trim() || '';\n return (title + ' ' + h1).toLowerCase().trim();\n}\n\nfunction titleToWordSet(title: string): Set<string> {\n return new Set(\n title.split(/\\s+/).filter(w => w.length > 1 && !STOP_WORDS.has(w))\n );\n}\n\nfunction jaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n let intersection = 0;\n for (const word of a) {\n if (b.has(word)) intersection++;\n }\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction checkContentCannibalization(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for cannibalization analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Collect pages: homepage + all blogSample\n const pages: Array<{ html: string; url: string }> = [\n { html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` },\n ];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Only homepage available - cannot assess content cannibalization', fix: 'Add blog/content pages to enable cross-page topic overlap analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Extract titles and word sets\n const pageTitles = pages.map(p => ({ title: extractPageTitle(p.html), url: p.url }));\n const wordSets = pageTitles.map(p => titleToWordSet(p.title));\n\n // Find site-wide common terms (appearing in >40% of pages).\n // These are \"site theme terms\" (e.g., \"medicare\" on a Medicare site) - NOT cannibalization signals.\n // A focused site SHOULD have many pages sharing core topic terms.\n const termPageCount = new Map<string, number>();\n for (const ws of wordSets) {\n for (const w of ws) {\n termPageCount.set(w, (termPageCount.get(w) || 0) + 1);\n }\n }\n const commonTermThreshold = Math.max(3, pages.length * 0.4);\n const siteThemeTerms = new Set<string>();\n for (const [term, count] of termPageCount) {\n if (count >= commonTermThreshold) siteThemeTerms.add(term);\n }\n\n // Filter word sets: remove site theme terms to compare only distinguishing words\n const filteredSets = wordSets.map(ws => {\n const filtered = new Set<string>();\n for (const w of ws) {\n if (!siteThemeTerms.has(w)) filtered.add(w);\n }\n return filtered;\n });\n\n // Pairwise comparison on FILTERED word sets (only distinguishing terms)\n const cannibalPairs: Array<{ urlA: string; urlB: string; similarity: number }> = [];\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n // Skip pairs where both have empty filtered sets (means both pages are purely about the core topic)\n if (filteredSets[i].size === 0 && filteredSets[j].size === 0) continue;\n const sim = jaccardSimilarity(filteredSets[i], filteredSets[j]);\n if (sim > 0.6) {\n cannibalPairs.push({\n urlA: pageTitles[i].url.slice(0, 60),\n urlB: pageTitles[j].url.slice(0, 60),\n similarity: Math.round(sim * 100),\n });\n }\n }\n }\n\n // Count unique URLs involved in cannibalization (not pairs)\n const cannibalUrls = new Set<string>();\n for (const pair of cannibalPairs) {\n cannibalUrls.add(pair.urlA);\n cannibalUrls.add(pair.urlB);\n }\n // Scale severity by the ratio of cannibalized pages to total pages\n const cannibalRatio = pages.length > 0 ? cannibalUrls.size / pages.length : 0;\n\n let score: number;\n if (cannibalPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no content cannibalization detected` });\n } else if (cannibalRatio <= 0.05) {\n // <5% of pages overlap - negligible\n score = 9;\n findings.push({ severity: 'info', detail: `${cannibalPairs.length} pair(s) of pages with minor topic overlap (${cannibalUrls.size}/${pages.length} pages affected)` });\n } else if (cannibalRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have overlapping topics`, fix: 'Differentiate titles and H1 headings to reduce topic overlap' });\n } else if (cannibalRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for overlapping topics`, fix: 'Consolidate overlapping pages or differentiate their titles and content focus' });\n } else if (cannibalRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have significant content overlap`, fix: 'Many pages compete for the same topics - consolidate or clearly differentiate them' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for the same topics`, fix: 'Severe content cannibalization - consolidate overlapping pages or create clear topic differentiation' });\n }\n\n // Detail findings for up to 3 pairs\n for (const pair of cannibalPairs.slice(0, 3)) {\n findings.push({ severity: 'low', detail: `Overlap (${pair.similarity}%): ${pair.urlA} vs ${pair.urlB}` });\n }\n\n // Cross-criterion adjustment: If topic coherence is very low (<= 4), cap cannibalization score.\n // \"No overlap\" because content is scattered (humidifiers, groceries, neuropathy) is NOT\n // the same as \"no overlap\" because content is well-differentiated within a focused niche.\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && score >= 8) {\n score = 6;\n findings.push({ severity: 'low', detail: 'Low topic overlap but content lacks coherent focus - not a strong signal for AI authority', fix: 'Focus content on fewer core topics to build topical authority that AI engines can identify' });\n }\n\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 26: Visible Date Signal ───────────────────────────────────────\n\nfunction checkVisibleDateSignal(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for date signal analysis' });\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // 1. Visible <time datetime=\"...\">text</time> elements\n const timeElements = combinedHtml.match(/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/gi) || [];\n const hasVisibleTime = timeElements.length > 0;\n if (hasVisibleTime) {\n score += 5;\n findings.push({ severity: 'info', detail: `${timeElements.length} visible <time> element(s) with datetime attribute found` });\n }\n\n // 2. datePublished/dateModified inside JSON-LD <script> blocks only\n const ldJsonBlocks = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const ldJsonText = ldJsonBlocks.join(' ');\n const hasDatePublished = /datePublished/i.test(ldJsonText);\n const hasDateModified = /dateModified/i.test(ldJsonText);\n const hasSchemaDate = hasDatePublished || hasDateModified;\n if (hasSchemaDate) {\n if (!hasVisibleTime) score += 7;\n else score += 5;\n const dateTypes = [hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(' + ');\n findings.push({ severity: 'info', detail: `JSON-LD schema contains ${dateTypes}` });\n }\n\n // 3. article:published_time / article:modified_time meta tags\n const hasMetaPublished = /<meta[^>]*property=\"article:published_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaModified = /<meta[^>]*property=\"article:modified_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaDate = hasMetaPublished || hasMetaModified;\n if (hasMetaDate && !hasVisibleTime && !hasSchemaDate) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Article meta tags with date information found' });\n } else if (hasMetaDate) {\n findings.push({ severity: 'info', detail: 'Article meta date tags also present (supplementary)' });\n }\n\n // Bonus: dateModified within 6 months\n if (hasDateModified) {\n const dateModMatch = ldJsonText.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (dateModMatch) {\n const modDate = new Date(dateModMatch[1]);\n if (!isNaN(modDate.getTime())) {\n const daysDiff = Math.floor((Date.now() - modDate.getTime()) / (1000 * 60 * 60 * 24));\n if (daysDiff <= 180) {\n score += 1;\n findings.push({ severity: 'info', detail: `dateModified is recent (${daysDiff} days ago) - freshness bonus applied` });\n }\n }\n }\n }\n\n score = Math.min(10, score);\n\n if (score === 0) {\n findings.push({ severity: 'high', detail: 'No visible date signals found (no <time> elements, no JSON-LD dates, no article meta dates)', fix: 'Add <time datetime=\"...\"> elements for user-visible dates and datePublished/dateModified to JSON-LD schema' });\n }\n\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Raw data summary extraction (for AI narrative) ─────────────────────────\n\nexport function extractRawDataSummary(data: SiteData): RawDataSummary {\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Schema types\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundSchemaTypes = schemaTypes.filter(t =>\n allSchemaText.includes(`\"${t}\"`) || allSchemaText.includes(`\"@type\":\"${t}\"`)\n );\n\n // Links\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n const externalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('http') && !href.includes(data.domain);\n });\n\n // robots.txt AI crawlers\n const robotsText = (data.robotsTxt?.text || '').toLowerCase();\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => robotsText.includes(c));\n const blockedCrawlers = mentionedCrawlers.filter(c => {\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(data.robotsTxt?.text || '');\n if (!match) return false;\n const section = match[1];\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n\n // Headings\n const hTagContent = (html.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n\n // Images\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n\n // Semantic elements\n const semanticChecks = ['main', 'article', 'nav', 'header', 'footer', 'section', 'time'];\n const foundElements = semanticChecks.filter(el => new RegExp(`<${el}[\\\\s>]`, 'i').test(html));\n\n return {\n domain: data.domain,\n protocol: data.protocol,\n homepage_length: html.length,\n homepage_text_length: text.trim().length,\n has_https: data.protocol === 'https',\n llms_txt_status: data.llmsTxt && !isHtmlResponse(data.llmsTxt) ? data.llmsTxt.status : null,\n llms_txt_length: data.llmsTxt?.status === 200 && !isHtmlResponse(data.llmsTxt) ? (data.llmsTxt.text.length) : 0,\n robots_txt_status: data.robotsTxt && !isHtmlResponse(data.robotsTxt) ? data.robotsTxt.status : null,\n robots_txt_snippet: (data.robotsTxt?.text || '').slice(0, 500),\n robots_txt_ai_crawlers: mentionedCrawlers,\n robots_txt_blocked_crawlers: blockedCrawlers,\n schema_types_found: foundSchemaTypes,\n schema_block_count: ldJsonMatches.length,\n faq_page_status: data.faqPage?.status ?? null,\n faq_page_length: data.faqPage?.status === 200 ? data.faqPage.text.length : 0,\n sitemap_status: data.sitemapXml?.status ?? null,\n internal_link_count: internalLinks.length,\n external_link_count: externalLinks.length,\n question_headings_count: questionHeadings.length,\n h1_count: h1Count,\n has_meta_description: /<meta[^>]*name=\"description\"[^>]*>/i.test(html),\n has_title: /<title[^>]*>[^<]+<\\/title>/i.test(html),\n has_phone: (() => {\n const phoneMatch = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text);\n if (!phoneMatch) return false;\n // Context validation: tel: link, schema telephone, or nearby keywords\n return /href=\"tel:/i.test(html) || /\"telephone\"/i.test(html) ||\n /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i.test(text);\n })(),\n has_address: /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text),\n has_org_schema: /organization|localbusiness/i.test(html),\n has_social_links: /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html),\n semantic_elements_found: foundElements,\n img_count: images.length,\n img_with_alt_count: imagesWithAlt.length,\n has_lang_attr: /lang=\"[a-z]{2}\"/i.test(html),\n has_aria: /role=\"|aria-/i.test(html),\n has_breadcrumbs: /breadcrumb|aria-label=\"breadcrumb\"/i.test(html),\n has_nav: /<nav[\\s>]/i.test(html),\n has_footer: /<footer[\\s>]/i.test(html),\n has_case_studies: /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i.test(text),\n has_statistics: /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i.test(text),\n has_expert_attribution: /written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(text),\n has_blog_section: /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i.test(html),\n // New criteria fields\n has_date_modified_schema: /dateModified/i.test(html),\n time_element_count: (html.match(/<time[\\s>]/gi) || []).length,\n sitemap_url_count: (data.sitemapXml?.text?.match(/<loc>/gi) || []).length,\n has_rss_feed: !!(data.rssFeed && data.rssFeed.status === 200 && !isHtmlResponse(data.rssFeed)),\n table_count: (html.match(/<table[\\s>]/gi) || []).length,\n ordered_list_count: (html.match(/<ol[\\s>]/gi) || []).length,\n unordered_list_count: (html.match(/<ul[\\s>]/gi) || []).length,\n definition_pattern_count: (text.match(/\\brefers?\\s+to\\b|\\bdefined\\s+as\\b|\\bknown\\s+as\\b/gi) || []).length,\n has_ai_txt: !!(data.aiTxt && data.aiTxt.status === 200 && !isHtmlResponse(data.aiTxt)),\n has_person_schema: /\"@type\"\\s*:\\s*\"Person\"/i.test(html),\n fact_data_point_count: (text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD)/g) || []).length,\n has_canonical: /<link[^>]*rel=\"canonical\"/i.test(html),\n has_license_schema: /license|copyrightHolder/i.test(html) && /application\\/ld\\+json/i.test(html),\n sitemap_recent_lastmod_count: (() => {\n const analysis = countRecentSitemapDates(data.sitemapXml?.text || '');\n return analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n })(),\n // Speakable schema fields\n has_speakable_schema: /speakablespecification/i.test(ldJsonMatches.join(' ')) || /\"speakable\"\\s*:/i.test(ldJsonMatches.join(' ')),\n speakable_selector_count: (ldJsonMatches.join(' ').match(/\"cssselector\"|\"xpath\"/gi) || []).length,\n // Blog sample fields\n blog_sample_count: data.blogSample?.length ?? 0,\n blog_sample_urls: data.blogSample?.map(p => p.finalUrl || '').filter(Boolean) ?? [],\n blog_sample_schema_types: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return [];\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogLd = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const blogSchema = blogLd.join(' ').toLowerCase();\n const types = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person'];\n return types.filter(t => blogSchema.includes(`\"${t}\"`));\n })(),\n blog_sample_question_headings: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return 0;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const hTags = (blogHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h)).length;\n })(),\n blog_sample_faq_schema_found: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return false;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n return /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n })(),\n // Criteria 24-26 fields\n question_heading_answer_rate: (() => {\n const combinedHtml = getCombinedHtml(data);\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length === 0) return -1;\n let answered = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim().length >= 20) answered++;\n }\n return Math.round((answered / qHeadings.length) * 100);\n })(),\n question_heading_total: extractQuestionHeadings(getCombinedHtml(data)).length,\n cannibalizing_pairs_count: (() => {\n const pages: Array<{ html: string }> = [{ html: data.homepage?.text || '' }];\n if (data.blogSample) for (const p of data.blogSample.slice(0, 5)) pages.push({ html: p.text });\n if (pages.length <= 1) return 0;\n const ws = pages.map(p => titleToWordSet(extractPageTitle(p.html)));\n let pairs = 0;\n for (let i = 0; i < ws.length; i++) {\n for (let j = i + 1; j < ws.length; j++) {\n if (jaccardSimilarity(ws[i], ws[j]) > 0.6) pairs++;\n }\n }\n return pairs;\n })(),\n page_titles_sampled: 1 + (data.blogSample?.length ?? 0),\n has_visible_date: (/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/i).test(getCombinedHtml(data)),\n has_schema_date_in_ld: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n return /datePublished|dateModified/i.test(ld);\n })(),\n date_modified_recency_days: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n const m = ld.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (!m) return null;\n const d = new Date(m[1]);\n if (isNaN(d.getTime())) return null;\n return Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24));\n })(),\n // Full-crawl stats\n crawl_discovered: data.crawlStats?.discovered ?? 0,\n crawl_fetched: data.crawlStats?.fetched ?? 0,\n crawl_skipped: data.crawlStats?.skipped ?? 0,\n };\n}\n\n// ─── Criterion 27: Topic Coherence ────────────────────────────────────────────\n\n/**\n * Extract meaningful terms from text (strip HTML, tokenize, remove stop words).\n */\nfunction extractTerms(html: string): string[] {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .toLowerCase();\n return text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n}\n\n/**\n * Extract core topic terms from homepage.\n * Uses H1, title tag, and meta description as the strongest signals.\n */\nfunction extractCoreTopicTerms(html: string): Set<string> {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const metaDescMatch = html.match(/<meta\\s+[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']+)[\"']/i)\n || html.match(/<meta\\s+[^>]*content=[\"']([^\"']+)[\"'][^>]*name=[\"']description[\"']/i);\n\n // Combine the most prominent text signals\n const prominentText = [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n metaDescMatch?.[1] || '',\n ].join(' ');\n\n // Also extract all H2 headings from homepage for broader topic context\n const h2Matches = html.match(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi) || [];\n const h2Text = h2Matches.map(h => h.replace(/<[^>]*>/g, '')).join(' ');\n\n const allTerms = extractTerms(prominentText + ' ' + h2Text);\n\n // Count term frequency and take top 25 most common\n const freq = new Map<string, number>();\n for (const t of allTerms) {\n freq.set(t, (freq.get(t) || 0) + 1);\n }\n\n const sorted = [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 25)\n .map(([term]) => term);\n\n return new Set(sorted);\n}\n\n/**\n * Extract title text from a page (title tag + H1 combined).\n */\nfunction getPageTopicText(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n ].join(' ').toLowerCase().trim();\n}\n\n/**\n * Extract bigrams (2-word phrases) from text.\n */\nfunction extractBigrams(text: string): string[] {\n const words = text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const bigrams: string[] = [];\n for (let i = 0; i < words.length - 1; i++) {\n bigrams.push(words[i] + ' ' + words[i + 1]);\n }\n return bigrams;\n}\n\n/**\n * Topic Coherence measures two things:\n * 1. Topic Focus: What % of pages share the dominant topic bigram\n * 2. Topic Scatter: How many distinct topic clusters exist\n *\n * A site with 90% of pages about \"Medicare coverage\" = high coherence\n * A site with pages about Medicare, humidifiers, groceries, neuropathy = low coherence\n */\nfunction checkTopicCoherence(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 0, status: 'not_found', findings, fix_priority: 'P0' };\n }\n\n if (!data.blogSample || data.blogSample.length < 3) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for topic coherence analysis` });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n\n // ─── Extract brand/domain words to exclude from topic analysis ──────────────\n // Compound domain names like \"miramace.com\" need to be split into component words.\n // We do a two-pass approach: first collect raw frequencies, then identify brand terms.\n const domainBase = data.domain.replace(/^www\\./, '').replace(/\\.(com|org|net|io|co|ai)$/i, '').toLowerCase();\n const brandWords = new Set<string>();\n brandWords.add(domainBase);\n // Split on hyphens/underscores\n for (const part of domainBase.split(/[-_]/)) {\n if (part.length > 2) brandWords.add(part);\n }\n\n // ─── Part 1: Topic Focus via dominant terms ─────────────────────────────────\n // First pass: count ALL terms (including potential brand words)\n const rawTermFreq = new Map<string, number>();\n const pageTitleTexts: string[] = [];\n\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n pageTitleTexts.push(topicText);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n rawTermFreq.set(w, (rawTermFreq.get(w) || 0) + 1);\n }\n }\n\n // Second pass: identify brand terms - words that appear in >80% of pages\n // AND are substrings of the domain name (e.g., \"mira\" is in \"miramace\")\n for (const [term, count] of rawTermFreq) {\n if (count / blogPages.length >= 0.80 && domainBase.includes(term)) {\n brandWords.add(term);\n }\n }\n\n // Third pass: rebuild frequencies without brand words\n const termFreq = new Map<string, number>();\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w) && !brandWords.has(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n termFreq.set(w, (termFreq.get(w) || 0) + 1);\n }\n }\n\n // Find the top terms that appear across the most pages\n const sortedTerms = [...termFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topTerm = sortedTerms[0];\n\n // ─── Part 2: Topic Scatter via unique topic clusters ────────────────────────\n // Extract meaningful bigrams from each page title and cluster them\n const bigramFreq = new Map<string, number>();\n const pageBigrams: string[][] = [];\n\n for (const topicText of pageTitleTexts) {\n const bigrams = extractBigrams(topicText)\n .filter(bg => !bg.split(' ').some(w => brandWords.has(w)));\n pageBigrams.push(bigrams);\n const uniqueBigrams = new Set(bigrams);\n for (const bg of uniqueBigrams) {\n bigramFreq.set(bg, (bigramFreq.get(bg) || 0) + 1);\n }\n }\n\n // Find dominant bigram\n const sortedBigrams = [...bigramFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topBigram = sortedBigrams[0];\n\n // Count distinct \"topic clusters\" - bigrams that appear in 2+ pages but are unique subjects\n // A focused site has 1-3 topic clusters; a scattered site has 10+\n const significantBigrams = sortedBigrams.filter(([, count]) => count >= 2);\n // Group overlapping bigrams (sharing a word) into clusters\n const clusterRoots: string[] = [];\n const assigned = new Set<string>();\n for (const [bg] of significantBigrams) {\n if (assigned.has(bg)) continue;\n clusterRoots.push(bg);\n assigned.add(bg);\n const [w1, w2] = bg.split(' ');\n // Mark overlapping bigrams as same cluster\n for (const [otherBg] of significantBigrams) {\n if (assigned.has(otherBg)) continue;\n if (otherBg.includes(w1) || otherBg.includes(w2)) {\n assigned.add(otherBg);\n }\n }\n }\n const topicClusterCount = clusterRoots.length;\n\n // Count pages that contain the dominant term\n const dominantTerm = topTerm?.[0] || '';\n const dominantTermCount = topTerm?.[1] || 0;\n const focusRatio = blogPages.length > 0 ? dominantTermCount / blogPages.length : 0;\n\n // Count pages that contain the dominant bigram\n const dominantBigram = topBigram?.[0] || '';\n const dominantBigramCount = topBigram?.[1] || 0;\n const bigramFocusRatio = blogPages.length > 0 ? dominantBigramCount / blogPages.length : 0;\n\n // ─── Scoring ────────────────────────────────────────────────────────────────\n // Primary signal: what % of pages share the dominant topic term.\n // Secondary signal: how many distinct topic clusters exist.\n // A Medicare site with varied products (\"scooter\", \"wheelchair\", \"walker\") is STILL coherent\n // if 80%+ of pages share \"medicare\" as the core topic.\n\n let score = 0;\n const bestFocusRatio = Math.max(focusRatio, bigramFocusRatio);\n\n // For sites with many pages, absolute term count matters more than ratio.\n // A term appearing on 15 of 60 pages (25%) is better than 2 of 5 pages (40%).\n const dominantPageCount = Math.max(dominantTermCount, dominantBigramCount);\n const hasStrongAbsolutePresence = dominantPageCount >= 10;\n\n // Focus score (0-7 points) - scale thresholds for large sites\n if (bestFocusRatio >= 0.80) {\n score += 7;\n } else if (bestFocusRatio >= 0.60) {\n score += 6;\n } else if (bestFocusRatio >= 0.45 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.30)) {\n score += 5;\n } else if (bestFocusRatio >= 0.30 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.20)) {\n score += 4;\n } else if (bestFocusRatio >= 0.15) {\n score += 2;\n } else {\n score += 1;\n }\n\n // Scatter adjustment (0-3 points for few clusters, penalty for many)\n // Scale cluster thresholds by page count - more pages naturally produce more clusters\n const clusterPenaltyReduced = focusRatio >= 0.70 || hasStrongAbsolutePresence;\n const scaledLow = Math.max(3, Math.floor(blogPages.length / 10));\n const scaledMid = Math.max(6, Math.floor(blogPages.length / 5));\n const scaledHigh = Math.max(10, Math.floor(blogPages.length / 3));\n\n if (topicClusterCount <= scaledLow) {\n score += 3;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic cluster(s) - tightly focused content` });\n } else if (topicClusterCount <= scaledMid) {\n score += clusterPenaltyReduced ? 2 : 1;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters${clusterPenaltyReduced ? ' within a focused niche' : ' - moderately focused'}` });\n } else if (topicClusterCount <= scaledHigh) {\n score += clusterPenaltyReduced ? 1 : 0;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters - scattered content`, fix: 'Reduce the number of distinct topics. Focus blog content on 2-3 core expertise areas.' });\n } else {\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters but strong core topic focus (${Math.round(focusRatio * 100)}%)` });\n }\n } else {\n score += clusterPenaltyReduced ? 0 : -2;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'medium', detail: `${topicClusterCount} topic clusters - highly scattered content`, fix: 'Content covers too many unrelated topics. AI engines cannot identify your expertise. Focus on your core niche.' });\n } else {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters despite strong core topic focus`, fix: 'Consider narrowing subtopics within your niche for even stronger AI visibility.' });\n }\n }\n\n score = Math.max(0, Math.min(10, score));\n\n // Report dominant topic\n if (dominantTerm) {\n const focusPct = Math.round(focusRatio * 100);\n findings.push({ severity: 'info', detail: `Dominant topic term: \"${dominantTerm}\" (${focusPct}% of ${blogPages.length} pages)` });\n }\n if (dominantBigram && dominantBigramCount >= 2) {\n findings.push({ severity: 'info', detail: `Dominant topic phrase: \"${dominantBigram}\" (${dominantBigramCount}/${blogPages.length} pages)` });\n }\n\n // Off-topic examples\n const offTopicExamples: string[] = [];\n for (let i = 0; i < pageTitleTexts.length && offTopicExamples.length < 3; i++) {\n if (dominantTerm && !pageTitleTexts[i].includes(dominantTerm)) {\n const title = blogPages[i].text.match(/<title[^>]*>([^<]+)<\\/title>/i)?.[1]?.trim();\n if (title && title.length > 3) offTopicExamples.push(title.slice(0, 60));\n }\n }\n if (offTopicExamples.length > 0 && score < 8) {\n findings.push({ severity: 'low', detail: `Off-topic examples: ${offTopicExamples.join('; ')}` });\n }\n\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// ─── Criterion 28: Content Depth ──────────────────────────────────────────────\n\nfunction countWords(html: string): number {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\nfunction countHeadings(html: string): number {\n const headings = html.match(/<h[2-6][^>]*>/gi) || [];\n return headings.length;\n}\n\nfunction checkContentDepth(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.blogSample || data.blogSample.length < 2) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for depth analysis` });\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: 3, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n const wordCounts = blogPages.map(p => countWords(p.text));\n const headingCounts = blogPages.map(p => countHeadings(p.text));\n\n const avgWords = wordCounts.reduce((a, b) => a + b, 0) / wordCounts.length;\n const avgHeadings = headingCounts.reduce((a, b) => a + b, 0) / headingCounts.length;\n const deepPages = wordCounts.filter(w => w >= 1000).length;\n const thinPages = wordCounts.filter(w => w < 300).length;\n const deepRatio = deepPages / blogPages.length;\n const thinRatio = thinPages / blogPages.length;\n\n let score = 0;\n\n // Word count scoring (0-5 points)\n if (avgWords >= 2000) {\n score += 5;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - excellent depth` });\n } else if (avgWords >= 1200) {\n score += 4;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - good depth` });\n } else if (avgWords >= 800) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page - moderate depth` });\n } else if (avgWords >= 400) {\n score += 2;\n findings.push({ severity: 'low', detail: `Average ${Math.round(avgWords)} words per page - shallow content`, fix: 'Expand articles with more detail, examples, and expert analysis to build AI citation authority' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Average ${Math.round(avgWords)} words per page - very thin content`, fix: 'Content is too thin for AI engines to cite. Aim for 1000+ words per article with structured sections.' });\n }\n\n // Heading structure scoring (0-3 points)\n if (avgHeadings >= 8) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - well-structured` });\n } else if (avgHeadings >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - decent structure` });\n } else if (avgHeadings >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page`, fix: 'Add more H2/H3 headings to break content into extractable sections' });\n } else {\n findings.push({ severity: 'medium', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - minimal structure`, fix: 'Add question-format H2/H3 headings so AI engines can extract specific answers' });\n }\n\n // Deep content ratio bonus (0-2 points)\n if (deepRatio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages (${Math.round(deepRatio * 100)}%) have 1000+ words` });\n } else if (deepRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages have 1000+ words` });\n }\n\n // Thin content penalty\n if (thinRatio >= 0.5) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: `${thinPages}/${blogPages.length} pages (${Math.round(thinRatio * 100)}%) have under 300 words - high thin content ratio`, fix: 'Remove or expand thin pages. Thin content dilutes site quality for AI engines.' });\n } else if (thinRatio >= 0.25) {\n score = Math.max(0, score - 1);\n findings.push({ severity: 'low', detail: `${thinPages}/${blogPages.length} pages have under 300 words` });\n }\n\n // Cross-criterion: deep content on scattered topics is less valuable for AI authority.\n // A 5000-word article about humidifiers doesn't help a healthcare company's AI citations.\n let finalScore = Math.min(10, score);\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && finalScore >= 8) {\n finalScore = 7;\n findings.push({ severity: 'low', detail: 'Deep content but low topic coherence - depth on scattered topics has reduced AI citation value', fix: 'Focus content depth on your core expertise area for maximum AI visibility' });\n }\n\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: finalScore, status: finalScore >= 7 ? 'pass' : finalScore >= 4 ? 'partial' : 'fail', findings, fix_priority: finalScore >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Main audit function ────────────────────────────────────────────────────\n\n/**\n * Run all 26 criteria checks using pre-fetched site data.\n * All functions are synchronous (no HTTP calls) - data was already fetched.\n */\nexport function auditSiteFromData(data: SiteData): CriterionResult[] {\n // Compute Topic Coherence first - used to cap Content Cannibalization\n const topicCoherence = checkTopicCoherence(data);\n const cannibalization = checkContentCannibalization(data, topicCoherence.score);\n\n return [\n checkLlmsTxt(data),\n checkSchemaMarkup(data),\n checkQAFormat(data),\n checkCleanHTML(data),\n checkEntityConsistency(data),\n checkRobotsTxt(data),\n checkFAQSection(data),\n checkOriginalData(data),\n checkInternalLinking(data),\n checkSemanticHTML(data),\n checkContentFreshness(data),\n checkSitemapCompleteness(data),\n checkRssFeed(data),\n checkTableListExtractability(data),\n checkDefinitionPatterns(data),\n checkDirectAnswerDensity(data),\n checkContentLicensing(data),\n checkAuthorSchemaDepth(data),\n checkFactDensity(data),\n checkCanonicalUrl(data),\n checkContentVelocity(data),\n checkSchemaCoverage(data),\n checkSpeakableSchema(data),\n checkQueryAnswerAlignment(data),\n cannibalization,\n checkVisibleDateSignal(data),\n topicCoherence,\n checkContentDepth(data, topicCoherence.score),\n ];\n}\n\n/**\n * Legacy entry point: fetches data and runs all checks.\n * Used by analyzer.ts for the /api/aeo/analyze endpoint.\n */\nexport async function auditSite(targetUrl: string): Promise<CriterionResult[]> {\n const url = new URL(targetUrl.startsWith('http') ? targetUrl : `https://${targetUrl}`);\n const domain = url.hostname.replace(/^www\\./, '');\n const data = await prefetchSiteData(domain);\n return auditSiteFromData(data);\n}\n","import type { CriterionResult } from './site-crawler.js';\n\nconst WEIGHTS: Record<string, number> = {\n // ─── Content Substance (~55%) ─────────────────────────────────────────────\n // WHY an AI engine would cite you. These drive citation quality directly.\n topic_coherence: 0.14, // Topical authority - THE gating signal\n original_data: 0.10, // Unique value AI can't find elsewhere\n content_depth: 0.07, // Comprehensive vs thin coverage\n fact_density: 0.06, // Information density per page\n direct_answer_density: 0.05, // Direct answers to queries\n qa_content_format: 0.05, // Answer-shaped content structure\n query_answer_alignment: 0.05, // Relevance to actual AI queries\n faq_section: 0.04, // Structured Q&A pairs\n\n // ─── Content Organization (~30%) ──────────────────────────────────────────\n // HOW easily AI engines can extract and trust your content.\n entity_consistency: 0.05, // Brand authority and E-E-A-T\n internal_linking: 0.04, // Site structure and topic clusters\n content_freshness: 0.04, // Recency signals\n schema_markup: 0.03, // Structured data for discovery\n author_schema_depth: 0.03, // Expert attribution\n table_list_extractability: 0.03, // Extractable structured data\n definition_patterns: 0.02, // Clear definitions\n visible_date_signal: 0.02, // Publication date trust\n semantic_html: 0.02, // Clean semantic structure\n clean_html: 0.02, // Parseable markup\n\n // ─── Technical Plumbing (~15%) ────────────────────────────────────────────\n // WHETHER AI crawlers can find you. Table stakes with diminishing returns.\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\nexport function calculateOverallScore(criteria: CriterionResult[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const c of criteria) {\n const weight = WEIGHTS[c.criterion] ?? 0.05;\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n if (totalWeight === 0) return 0;\n\n let score = Math.round(weightedSum / totalWeight);\n\n // ─── Coherence Gate ─────────────────────────────────────────────────────\n // Topic coherence is a multiplicative signal, not just additive. A site\n // with scattered, unfocused content cannot achieve a high AEO score\n // regardless of technical perfection - AI engines won't build topical\n // trust from a grab-bag of unrelated posts.\n //\n // Thresholds map to score tiers:\n // coherence 0-1: max 35-40 (Critical gaps)\n // coherence 2-4: max 45-55 (Below average)\n // coherence 5: max 60 (Moderate ceiling)\n // coherence ≥ 6: no cap\n const coherence = criteria.find(c => c.criterion === 'topic_coherence');\n if (coherence && coherence.score < 6) {\n const cap = 35 + coherence.score * 5;\n score = Math.min(score, cap);\n }\n\n return score;\n}\n","/**\r\n * SPA detection and headless Chromium rendering for pre-crawl.\r\n *\r\n * When a site returns a thin JS-only shell (e.g. React CRA, Vite SPA),\r\n * the regular fetch() gets almost no text content, causing false low scores.\r\n * This module detects those shells and re-renders them with Puppeteer.\r\n */\r\n\r\nimport type { FetchResult } from './site-crawler.js';\r\n\r\n// ─── SPA classification ─────────────────────────────────────────────────────\r\n\r\nexport type RenderingMethod = 'server' | 'client-spa';\r\n\r\ninterface RenderingClassification {\r\n method: RenderingMethod;\r\n framework: string | null;\r\n}\r\n\r\n// ─── SPA shell detection ────────────────────────────────────────────────────\r\n\r\nconst SPA_INDICATORS = [\r\n // Root mount points (empty or nearly empty, including self-closing)\r\n /<div\\s+id=[\"'](root|app|__next|__nuxt|__vue)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i,\r\n // Framework globals\r\n /__NEXT_DATA__/,\r\n /__NUXT__/,\r\n // CRA / Vite bundle patterns\r\n /src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i,\r\n /src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i,\r\n // React markers\r\n /data-reactroot/i,\r\n // Angular\r\n /ng-version/i,\r\n // Noscript JS warnings\r\n /<noscript>[^<]*(?:javascript|enable\\s+js|requires?\\s+javascript)[^<]*<\\/noscript>/i,\r\n];\r\n\r\n/**\r\n * Detect whether raw HTML is a thin SPA shell that needs client-side rendering.\r\n * Both conditions required:\r\n * 1. Visible text content < 500 chars (thin page)\r\n * 2. At least one SPA framework indicator present\r\n */\r\nexport function isSpaShell(html: string): boolean {\r\n // Strip all tags and collapse whitespace to get visible text\r\n const text = html\r\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\r\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\r\n .replace(/<[^>]*>/g, ' ')\r\n .replace(/\\s+/g, ' ')\r\n .trim();\r\n\r\n if (text.length >= 500) return false;\r\n\r\n return SPA_INDICATORS.some((pattern) => pattern.test(html));\r\n}\r\n\r\n/**\r\n * Classify a page's rendering method from its raw (non-headless) HTML.\r\n * Returns the method ('server' | 'client-spa') and detected framework if any.\r\n */\r\nexport function classifyRendering(html: string): RenderingClassification {\r\n if (!isSpaShell(html)) return { method: 'server', framework: null };\r\n\r\n // Detect framework from SPA indicators\r\n const frameworkPatterns: [RegExp, string][] = [\r\n [/__NEXT_DATA__/, 'next'],\r\n [/__NUXT__/, 'nuxt'],\r\n [/<div\\s+id=[\"']__vue[\"']/i, 'vue'],\r\n [/ng-version/i, 'angular'],\r\n [/data-reactroot/i, 'react'],\r\n [/<div\\s+id=[\"'](root|app)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i, 'react'],\r\n [/src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i, 'react'],\r\n [/src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i, 'vite'],\r\n ];\r\n\r\n for (const [pattern, framework] of frameworkPatterns) {\r\n if (pattern.test(html)) return { method: 'client-spa', framework };\r\n }\r\n\r\n return { method: 'client-spa', framework: null };\r\n}\r\n\r\n// ─── Headless Chromium rendering ────────────────────────────────────────────\r\n\r\nexport interface HeadlessOptions {\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Render a URL with headless Chromium and return the fully-rendered HTML.\r\n * Returns null if Puppeteer is not installed or any error occurs.\r\n * The caller should fall back to the raw HTML in that case.\r\n */\r\nexport async function fetchWithHeadless(\r\n url: string,\r\n options?: HeadlessOptions\r\n): Promise<FetchResult | null> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let puppeteer: any;\r\n try {\r\n // Dynamic import - puppeteer is an optional peer dependency\r\n const mod = 'puppeteer';\r\n puppeteer = await import(/* @vite-ignore */ mod);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const timeout = options?.timeout ?? 25000;\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let browser: any = null;\r\n\r\n try {\r\n browser = await puppeteer.launch({\r\n headless: true,\r\n args: [\r\n '--no-sandbox',\r\n '--disable-setuid-sandbox',\r\n '--disable-dev-shm-usage',\r\n '--disable-gpu',\r\n '--single-process',\r\n ],\r\n });\r\n\r\n const page = await browser.newPage();\r\n\r\n // Block heavy resources to speed up rendering\r\n await page.setRequestInterception(true);\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n page.on('request', (req: any) => {\r\n const type = req.resourceType();\r\n if (['image', 'font', 'media', 'stylesheet'].includes(type)) {\r\n req.abort();\r\n } else {\r\n req.continue();\r\n }\r\n });\r\n\r\n await page.setUserAgent('AEO-Visibility-Bot/1.0');\r\n await page.goto(url, { waitUntil: 'networkidle2', timeout });\r\n\r\n // Wait for JS to populate the body with real text\r\n try {\r\n await page.waitForFunction(\r\n 'document.body && document.body.innerText && document.body.innerText.replace(/\\\\s+/g, \" \").trim().length > 100',\r\n { timeout: 5000 }\r\n );\r\n } catch {\r\n // Body text never exceeded 100 chars - still return what we got\r\n }\r\n\r\n const html = await page.content();\r\n const finalUrl = page.url();\r\n\r\n return {\r\n text: html.slice(0, 500000),\r\n status: 200,\r\n finalUrl,\r\n };\r\n } catch {\r\n return null;\r\n } finally {\r\n if (browser) {\r\n try {\r\n await browser.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n }\r\n }\r\n}\r\n","/**\n * Shared scorecard building functions.\n * Extracted from cli/pre-crawl.ts for reuse in instant-audit and other consumers.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { Status, FindingType, Severity, ScoreCardItem, CriterionDetail, DetailedFinding } from './types.js';\nimport type { AuditFinding } from './types.js';\n\n// ─── Criterion label mapping (site-crawler labels -> prompt-standard names) ──\n\nexport const CRITERION_LABELS: Record<string, string> = {\n 'llms.txt File': 'llms.txt File',\n 'Schema.org Structured Data': 'Schema.org Structured Data',\n 'Q&A Content Format': 'Q&A Content Format',\n 'Clean, Crawlable HTML': 'Clean, Crawlable HTML',\n 'Entity Authority & E-E-A-T': 'Entity Authority & NAP Consistency',\n 'robots.txt for AI Crawlers': 'robots.txt for AI Crawlers',\n 'Comprehensive FAQ Sections': 'Comprehensive FAQ Section',\n 'Original Data & Expert Content': 'Original Data & Expert Analysis',\n 'Internal Linking Architecture': 'Internal Linking Structure',\n 'Semantic HTML5 & Accessibility': 'Semantic HTML5 & Accessibility',\n 'Content Freshness Signals': 'Content Freshness Signals',\n 'Sitemap Completeness': 'Sitemap Completeness',\n 'RSS/Atom Feed': 'RSS/Atom Feed',\n 'Table & List Extractability': 'Table & List Extractability',\n 'Definition Patterns': 'Definition Patterns',\n 'Direct Answer Paragraphs': 'Direct Answer Paragraphs',\n 'Content Licensing & AI Permissions': 'Content Licensing & AI Permissions',\n 'Author & Expert Schema': 'Author & Expert Schema',\n 'Fact & Data Density': 'Fact & Data Density',\n 'Canonical URL Strategy': 'Canonical URL Strategy',\n 'Content Publishing Velocity': 'Content Publishing Velocity',\n 'Schema Coverage & Depth': 'Schema Coverage & Depth',\n 'Speakable Schema': 'Speakable Schema',\n 'Query-Answer Alignment': 'Query-Answer Alignment',\n 'Content Cannibalization': 'Content Cannibalization',\n 'Visible Date Signal': 'Visible Date Signal',\n 'Topic Coherence': 'Topic Coherence',\n 'Content Depth': 'Content Depth',\n};\n\n// ─── Score to Status mapping (matches AI prompt: 0=MISSING...8-10=STRONG) ────\n\nexport function scoreToStatus(score: number): Status {\n if (score === 0) return 'MISSING';\n if (score === 1) return 'NEARLY EMPTY';\n if (score === 2) return 'POOR';\n if (score === 3) return 'WEAK';\n if (score <= 5) return 'PARTIAL';\n if (score === 6) return 'MODERATE';\n if (score === 7) return 'GOOD';\n return 'STRONG';\n}\n\n// ─── Finding severity mapping (site-crawler -> audit format) ─────────────────\n\nexport function mapFindingSeverity(severity: AuditFinding['severity']): Severity {\n switch (severity) {\n case 'critical': return 'CRITICAL';\n case 'high': return 'MISSING';\n case 'medium': return 'ADD';\n case 'low': return 'PARTIAL';\n case 'info': return 'WORKING';\n default: return 'PARTIAL';\n }\n}\n\nexport function mapFindingType(severity: AuditFinding['severity'], hasFix: boolean): FindingType {\n if (severity === 'info') return 'Good';\n if (severity === 'critical') return 'Critical';\n if (severity === 'high') return 'Missing';\n if (hasFix) return 'Issue';\n return 'Note';\n}\n\n// ─── Convert CriterionResult to scorecard + detailedFindings format ──────────\n\nexport function buildScorecard(results: CriterionResult[]): ScoreCardItem[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n // Build keyFindings from the most important findings (2-3 sentences)\n const keyParts: string[] = [];\n for (const f of r.findings) {\n if (keyParts.length >= 3) break;\n keyParts.push(f.detail);\n }\n const keyFindings = keyParts.join('. ') + (keyParts.length > 0 && !keyParts[keyParts.length - 1].endsWith('.') ? '.' : '');\n\n return {\n id: i + 1,\n criterion: label,\n score: r.score,\n status: scoreToStatus(r.score),\n keyFindings,\n };\n });\n}\n\nexport function buildDetailedFindings(results: CriterionResult[]): CriterionDetail[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n const rawFindings: DetailedFinding[] = r.findings.map(f => ({\n type: mapFindingType(f.severity, !!f.fix),\n description: f.fix ? `${f.detail}. ${f.fix}` : f.detail,\n severity: mapFindingSeverity(f.severity),\n }));\n\n // Deduplicate findings by description\n const seen = new Set<string>();\n const findings: DetailedFinding[] = [];\n for (const f of rawFindings) {\n if (!seen.has(f.description)) {\n seen.add(f.description);\n findings.push(f);\n }\n }\n\n // Fallback: if a criterion somehow has fewer than 2 findings, add a single specific one\n if (findings.length < 2) {\n if (r.score >= 7) {\n findings.push({ type: 'Good', description: `${label} is well-implemented for AI engine visibility.`, severity: 'WORKING' });\n } else {\n findings.push({ type: 'Note', description: `${label} needs improvement - review specific issues above.`, severity: 'PARTIAL' });\n }\n }\n\n return {\n id: i + 1,\n name: label,\n findings,\n };\n });\n}\n","/**\n * Deterministic narrative generation from scorecard data.\n * Produces verdict, opportunities, pitchNumbers, and bottomLine\n * without any LLM calls - pure template-based generation.\n */\n\nimport type { ScoreCardItem, Deliverable, PitchMetric, ImpactLevel } from './types.js';\nimport type { CriterionResult } from './site-crawler.js';\nimport type { RawDataSummary } from './site-crawler.js';\n\n// ─── Scoring weights (mirrored from scoring.ts for impact calculation) ───────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.05,\n query_answer_alignment: 0.05,\n faq_section: 0.04,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n definition_patterns: 0.02,\n visible_date_signal: 0.02,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\n// ─── Opportunity templates (28 criteria) ─────────────────────────────────────\n\ninterface OpportunityTemplate {\n name: string;\n effort: string;\n description: string;\n}\n\nconst OPPORTUNITY_TEMPLATES: Record<string, OpportunityTemplate> = {\n llms_txt: {\n name: 'Create llms.txt File',\n effort: 'Low',\n description: 'Add a /llms.txt file that describes your site, core services, and key pages in markdown format. This helps AI engines like ChatGPT and Claude understand your site structure and content offerings.',\n },\n schema_markup: {\n name: 'Add Schema.org Structured Data',\n effort: 'Medium',\n description: 'Implement JSON-LD structured data (Organization, Service, Product, FAQPage) on key pages. Schema markup helps AI engines extract and cite your content accurately.',\n },\n qa_content_format: {\n name: 'Restructure Content as Q&A',\n effort: 'Medium',\n description: 'Add question-based headings (H2/H3) throughout your content. Use \"What is...\", \"How does...\", \"Why should...\" patterns that match how users query AI assistants.',\n },\n clean_html: {\n name: 'Fix HTML Structure & Enable HTTPS',\n effort: 'Medium',\n description: 'Ensure clean, well-structured HTML with proper meta tags, semantic elements, and HTTPS. Clean HTML makes your content more parseable by AI crawlers.',\n },\n entity_consistency: {\n name: 'Strengthen Entity Authority (NAP)',\n effort: 'Low',\n description: 'Add Organization schema with consistent name, address, phone (NAP). Include sameAs links to social profiles and authoritative directories to strengthen entity recognition.',\n },\n robots_txt: {\n name: 'Configure robots.txt for AI Crawlers',\n effort: 'Low',\n description: 'Update robots.txt to explicitly allow AI crawlers (GPTBot, ClaudeBot, PerplexityBot). Add a Sitemap directive to help crawlers discover your content.',\n },\n faq_section: {\n name: 'Build Comprehensive FAQ Section',\n effort: 'Medium',\n description: 'Create a dedicated FAQ page with FAQPage schema markup. Cover common questions about your products, services, and industry to become a direct answer source for AI engines.',\n },\n original_data: {\n name: 'Add Original Data & Case Studies',\n effort: 'High',\n description: 'Publish original research, statistics, case studies, or proprietary data that AI engines can cite. Unique data points make your content a primary source rather than a derivative one.',\n },\n internal_linking: {\n name: 'Improve Internal Linking Architecture',\n effort: 'Medium',\n description: 'Strengthen internal linking with descriptive anchor text between related pages. Add breadcrumb navigation and ensure every key page is reachable within 3 clicks from the homepage.',\n },\n semantic_html: {\n name: 'Implement Semantic HTML5',\n effort: 'Low',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure. Add lang attribute and ARIA labels for accessibility.',\n },\n content_freshness: {\n name: 'Add Content Freshness Signals',\n effort: 'Low',\n description: 'Include dateModified schema, visible last-updated dates, and time elements on content pages. Fresh content signals help AI engines prioritize your pages over stale alternatives.',\n },\n sitemap_completeness: {\n name: 'Create Complete Sitemap',\n effort: 'Low',\n description: 'Generate a comprehensive sitemap.xml with lastmod dates for all important pages. A complete sitemap ensures AI crawlers can discover and prioritize your full content library.',\n },\n rss_feed: {\n name: 'Deploy RSS/Atom Feed',\n effort: 'Low',\n description: 'Add an RSS or Atom feed linked from your homepage. Feeds signal active content publishing and give AI engines a structured way to track your latest content.',\n },\n table_list_extractability: {\n name: 'Add Structured Tables & Lists',\n effort: 'Medium',\n description: 'Use HTML tables for comparison data and ordered/unordered lists for features, steps, and specifications. Structured data formats are directly extractable by AI engines for answers.',\n },\n definition_patterns: {\n name: 'Add Definition-Style Content',\n effort: 'Low',\n description: 'Include clear definition patterns (\"X refers to...\", \"X is defined as...\") for key terms and concepts. Definition-style content is highly citable by AI engines answering \"what is\" queries.',\n },\n direct_answer_density: {\n name: 'Add Direct Answer Paragraphs',\n effort: 'Medium',\n description: 'Write concise, standalone answer paragraphs (2-3 sentences) immediately after question headings. These \"snippet-ready\" paragraphs are ideal for AI engine citations.',\n },\n content_licensing: {\n name: 'Add Content Licensing & ai.txt',\n effort: 'Low',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to your structured data. Clear licensing signals help AI engines understand how they can use your content.',\n },\n author_schema_depth: {\n name: 'Enhance Author & Expert Schema',\n effort: 'Low',\n description: 'Add Person schema for content authors with credentials, expertise, and sameAs links. Expert attribution strengthens E-E-A-T signals that AI engines use to evaluate source credibility.',\n },\n fact_density: {\n name: 'Increase Fact & Data Density',\n effort: 'Medium',\n description: 'Add specific numbers, percentages, statistics, and data points throughout your content. Fact-dense content gives AI engines concrete data to cite rather than vague claims.',\n },\n canonical_url: {\n name: 'Fix Canonical URL Strategy',\n effort: 'Low',\n description: 'Add rel=\"canonical\" tags to all pages pointing to the preferred URL version. Canonical URLs prevent duplicate content confusion and consolidate AI engine citations to a single authoritative URL.',\n },\n content_velocity: {\n name: 'Increase Publishing Frequency',\n effort: 'High',\n description: 'Establish a regular content publishing cadence with dated entries in your sitemap. Consistent publishing signals to AI engines that your site is an active, current information source.',\n },\n schema_coverage: {\n name: 'Deepen Schema Coverage',\n effort: 'Medium',\n description: 'Extend structured data beyond the homepage to inner pages (articles, services, products). Consistent schema coverage across your site helps AI engines understand your full content depth.',\n },\n speakable_schema: {\n name: 'Add Speakable Schema',\n effort: 'Low',\n description: 'Add SpeakableSpecification schema with CSS selectors pointing to key content sections. This tells voice assistants and AI engines which parts of your page are most suitable for spoken answers.',\n },\n query_answer_alignment: {\n name: 'Improve Question-Answer Alignment',\n effort: 'Medium',\n description: 'Ensure every question-format heading (H2/H3) is followed by a direct answer paragraph. This pattern is ideal for AI engine snippet extraction.',\n },\n content_cannibalization: {\n name: 'Resolve Content Cannibalization',\n effort: 'Medium',\n description: 'Multiple pages compete for the same topic. Consolidate overlapping pages or differentiate titles and H1 headings.',\n },\n visible_date_signal: {\n name: 'Add Visible Date Signals',\n effort: 'Low',\n description: 'Display publication/modification dates visibly using <time> elements and add datePublished/dateModified to JSON-LD schema.',\n },\n topic_coherence: {\n name: 'Focus Content on Core Topics',\n effort: 'High',\n description: 'Ensure blog content consistently covers your core expertise areas rather than scattering across unrelated topics. AI engines build authority models - a site about \"Medicare coverage\" that also publishes about humidifiers and groceries dilutes its topical authority.',\n },\n content_depth: {\n name: 'Increase Content Depth',\n effort: 'Medium',\n description: 'Expand articles to 1000+ words with structured H2/H3 sections, comparison tables, and expert analysis. Thin content (under 300 words) is rarely cited by AI engines. Deep, well-structured articles demonstrate expertise.',\n },\n};\n\n// ─── Impact calculation ──────────────────────────────────────────────────────\n\nfunction calculateImpact(score: number, weight: number, effort: string): ImpactLevel {\n const impactScore = (10 - score) * weight * 100;\n\n // Quick win override: low effort + meaningful impact\n if (effort === 'Low' && impactScore >= 3) return 'QUICK WIN';\n\n if (impactScore >= 12) return 'CRITICAL';\n if (impactScore >= 8) return 'HIGH';\n if (impactScore >= 5) return 'CORE AEO';\n if (impactScore >= 3) return 'MEDIUM';\n return 'LOW';\n}\n\n// ─── Verdict generation ──────────────────────────────────────────────────────\n\nexport function generateVerdict(\n score: number,\n scorecard: ScoreCardItem[],\n rawData: RawDataSummary,\n domain: string\n): string {\n // Score-tier opening\n let opening: string;\n if (score >= 86) {\n opening = `Excellent AEO implementation scoring ${score}/100.`;\n } else if (score >= 71) {\n opening = `Strong AEO fundamentals scoring ${score}/100 with room for optimization.`;\n } else if (score >= 56) {\n opening = `Moderate AEO readiness at ${score}/100 with significant gaps to address.`;\n } else if (score >= 41) {\n opening = `Below-average AEO readiness at ${score}/100 - multiple areas need attention.`;\n } else {\n opening = `Critical AEO gaps at ${score}/100 - ${domain} is largely invisible to AI engines.`;\n }\n\n // Top 3 strengths (score >= 8)\n const strengths = scorecard\n .filter(s => s.score >= 8)\n .sort((a, b) => b.score - a.score)\n .slice(0, 3);\n\n // Bottom 3 weaknesses (score <= 4)\n const weaknesses = scorecard\n .filter(s => s.score <= 4)\n .sort((a, b) => a.score - b.score)\n .slice(0, 3);\n\n const parts = [opening];\n\n if (strengths.length > 0) {\n const names = strengths.map(s => s.criterion);\n parts.push(`Key strengths include ${formatList(names)}.`);\n }\n\n if (weaknesses.length > 0) {\n const names = weaknesses.map(s => s.criterion);\n parts.push(`Priority gaps: ${formatList(names)}.`);\n }\n\n // Protocol note\n if (!rawData.has_https) {\n parts.push('HTTPS is not enabled, which caps several criteria scores and reduces AI crawler trust.');\n }\n\n // SPA rendering note\n if (rawData.rendered_with_headless) {\n parts.push('Note: this site uses client-side JavaScript rendering. AI crawlers see an empty page shell instead of content, which is the primary factor limiting the score.');\n }\n\n return parts.join(' ');\n}\n\n// ─── Opportunities generation ────────────────────────────────────────────────\n\nexport function generateOpportunities(\n scorecard: ScoreCardItem[],\n criterionResults: CriterionResult[]\n): Deliverable[] {\n const candidates: Array<{\n criterion: string;\n score: number;\n weight: number;\n impactScore: number;\n template: OpportunityTemplate;\n impact: ImpactLevel;\n }> = [];\n\n for (const result of criterionResults) {\n if (result.score > 7) continue; // Only generate opportunities for scores <= 7\n\n const weight = CRITERION_WEIGHTS[result.criterion] ?? 0.05;\n const template = OPPORTUNITY_TEMPLATES[result.criterion];\n if (!template) continue;\n\n const impactScore = (10 - result.score) * weight * 100;\n const impact = calculateImpact(result.score, weight, template.effort);\n\n candidates.push({\n criterion: result.criterion,\n score: result.score,\n weight,\n impactScore,\n template,\n impact,\n });\n }\n\n // Sort by impact score descending (highest impact first)\n candidates.sort((a, b) => b.impactScore - a.impactScore);\n\n // Take top 8-10\n const top = candidates.slice(0, 10);\n\n return top.map((c, i) => ({\n id: i + 1,\n name: c.template.name,\n description: c.template.description,\n effort: c.template.effort,\n impact: c.impact,\n }));\n}\n\n// ─── Pitch numbers generation ────────────────────────────────────────────────\n\nexport function generatePitchNumbers(\n score: number,\n rawData: RawDataSummary,\n scorecard: ScoreCardItem[]\n): PitchMetric[] {\n const metrics: PitchMetric[] = [];\n\n // 0. Rendering method (SPA warning - prepended so it appears first)\n if (rawData.rendered_with_headless) {\n metrics.push({\n metric: 'Rendering Method',\n value: 'Client-Side Only',\n significance: 'AI crawlers see empty HTML. All content loads via JavaScript, making this site invisible to ChatGPT, Claude, and Perplexity.',\n });\n }\n\n // 1. Overall AEO Score\n metrics.push({\n metric: 'AEO Score',\n value: `${score}/100`,\n significance: score >= 70\n ? 'Above average AI engine visibility'\n : score >= 50\n ? 'Moderate AI visibility with clear improvement paths'\n : 'Below average - significant optimization needed',\n });\n\n // 2. Schema types found\n const schemaCount = rawData.schema_types_found.length;\n metrics.push({\n metric: 'Schema Types',\n value: `${schemaCount} found`,\n significance: schemaCount >= 4\n ? 'Rich structured data helps AI engines parse content'\n : schemaCount >= 1\n ? 'Basic schema present but more types would improve AI extraction'\n : 'No structured data - AI engines cannot reliably extract content',\n });\n\n // 3. AI crawler readiness\n const aiCrawlerCount = rawData.robots_txt_ai_crawlers.length;\n const blockedCount = rawData.robots_txt_blocked_crawlers.length;\n metrics.push({\n metric: 'AI Crawler Access',\n value: blockedCount > 0\n ? `${blockedCount} blocked`\n : aiCrawlerCount > 0\n ? `${aiCrawlerCount} configured`\n : 'Not configured',\n significance: blockedCount > 0\n ? 'Active AI crawlers are blocked from accessing content'\n : aiCrawlerCount > 0\n ? 'robots.txt explicitly addresses AI crawler access'\n : 'No AI-specific crawler directives in robots.txt',\n });\n\n // 4. Content pages indexed\n const sitemapUrls = rawData.sitemap_url_count;\n metrics.push({\n metric: 'Sitemap URLs',\n value: sitemapUrls > 0 ? `${sitemapUrls} pages` : 'No sitemap',\n significance: sitemapUrls >= 50\n ? 'Comprehensive content library discoverable by AI crawlers'\n : sitemapUrls >= 10\n ? 'Moderate content footprint in sitemap'\n : sitemapUrls > 0\n ? 'Small sitemap - expanding content improves AI coverage'\n : 'No sitemap means AI crawlers must discover pages via links only',\n });\n\n // 5. Internal linking\n const linkCount = rawData.internal_link_count;\n metrics.push({\n metric: 'Internal Links',\n value: `${linkCount} links`,\n significance: linkCount >= 30\n ? 'Strong internal linking supports AI content discovery'\n : linkCount >= 10\n ? 'Moderate linking - adding more cross-references improves navigability'\n : 'Weak internal linking limits AI crawler depth',\n });\n\n // 6. Question headings\n const questionCount = rawData.question_headings_count + (rawData.blog_sample_question_headings || 0);\n if (questionCount > 0) {\n metrics.push({\n metric: 'Question Headings',\n value: `${questionCount} found`,\n significance: 'Question-based headings match how users query AI assistants',\n });\n }\n\n // 7. Criteria passing (>= 7)\n const passing = scorecard.filter(s => s.score >= 7).length;\n metrics.push({\n metric: 'Criteria Passing',\n value: `${passing}/28`,\n significance: passing >= 18\n ? 'Excellent coverage across AEO dimensions'\n : passing >= 12\n ? 'Good foundation with room to improve remaining criteria'\n : `${26 - passing} criteria need attention for full AI visibility`,\n });\n\n return metrics;\n}\n\n// ─── Bottom line generation ──────────────────────────────────────────────────\n\nexport function generateBottomLine(\n score: number,\n opportunities: Deliverable[],\n scorecard: ScoreCardItem[],\n domain: string\n): string {\n const quickWins = opportunities.filter(o => o.impact === 'QUICK WIN');\n const criticalOps = opportunities.filter(o => o.impact === 'CRITICAL' || o.impact === 'HIGH');\n\n const passing = scorecard.filter(s => s.score >= 7).length;\n const total = scorecard.length;\n\n let summary: string;\n if (score >= 86) {\n summary = `${domain} demonstrates excellent AI engine optimization with ${passing}/${total} criteria at good or strong levels. Focus on maintaining content freshness and expanding structured data coverage to stay ahead.`;\n } else if (score >= 71) {\n summary = `${domain} has a solid AEO foundation with ${passing}/${total} criteria passing.`;\n if (quickWins.length > 0) {\n summary += ` ${quickWins.length} quick wins available: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n if (criticalOps.length > 0) {\n summary += ` Address ${criticalOps.length} high-impact opportunities to push the score above 85.`;\n }\n } else if (score >= 56) {\n summary = `${domain} has moderate AI visibility with ${passing}/${total} criteria passing. ${opportunities.length} improvement opportunities identified.`;\n if (quickWins.length > 0) {\n summary += ` Start with quick wins: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n } else if (score >= 41) {\n summary = `${domain} needs significant AEO work with only ${passing}/${total} criteria passing.`;\n if (criticalOps.length > 0) {\n summary += ` Priority: ${criticalOps.slice(0, 3).map(c => c.name).join(', ')}.`;\n }\n summary += ` Implementing the top ${Math.min(5, opportunities.length)} recommendations could improve the score by 15-25 points.`;\n } else {\n summary = `${domain} is largely invisible to AI engines with only ${passing}/${total} criteria passing. Fundamental AEO infrastructure is missing.`;\n if (opportunities.length > 0) {\n summary += ` Start with: ${opportunities.slice(0, 3).map(o => o.name).join(', ')}.`;\n }\n summary += ` A comprehensive AEO implementation could transform AI visibility from near-zero to competitive.`;\n }\n\n return summary;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return '';\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;\n}\n","/**\n * Extended page discovery for instant audit.\n * Fetches additional pages beyond what prefetchSiteData provides,\n * including nav-linked pages, common paths, and content pages from sitemap.\n */\n\nimport type { FetchResult, SiteData, PageCategory } from './site-crawler.js';\n\n// ─── Fetch helper (matches site-crawler.ts fetchText) ────────────────────────\n\nasync function fetchPage(url: string, timeoutMs = 10000): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(timeoutMs),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n if (res.status !== 200) return null;\n const text = await res.text();\n if (text.length < 200) return null; // Skip trivially small pages\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n// ─── Page variant paths ──────────────────────────────────────────────────────\n\nconst PAGE_VARIANTS: Record<string, string[]> = {\n about: ['/about', '/about-us', '/company', '/who-we-are'],\n pricing: ['/pricing', '/plans', '/packages'],\n services: ['/services', '/features', '/solutions', '/products', '/what-we-do'],\n contact: ['/contact', '/contact-us', '/get-in-touch'],\n team: ['/team', '/our-team', '/authors', '/people', '/leadership'],\n resources: ['/resources', '/resource-center', '/library'],\n docs: ['/docs', '/documentation', '/help', '/help-center', '/support'],\n cases: ['/case-studies', '/customers', '/success-stories', '/testimonials'],\n};\n\n// ─── Nav link extraction ─────────────────────────────────────────────────────\n\n/**\n * Extract internal page paths from <nav> elements in homepage HTML.\n * Returns deduplicated absolute paths (e.g. ['/about', '/pricing']).\n */\nexport function extractNavLinks(html: string, domain: string): string[] {\n // Extract links from <nav> elements\n const navBlocks = html.match(/<nav[\\s\\S]*?<\\/nav>/gi) || [];\n const navHtml = navBlocks.join('\\n');\n\n const hrefMatches = navHtml.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize: strip trailing slash, skip root and fragments\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#')) continue;\n\n // Skip resource/utility paths\n if (/\\.(js|css|png|jpg|svg|ico|pdf|xml|txt)$/i.test(path)) continue;\n if (/^\\/(api|wp-|static|assets|_next|auth|login|signup|cart|checkout)\\b/i.test(path)) continue;\n\n paths.add(path);\n }\n\n return Array.from(paths);\n}\n\n// ─── Full-page link extraction (fallback when no sitemap) ─────────────────\n\n/**\n * Extract ALL internal links from the full page HTML (not just <nav>).\n * Used as a fallback when no sitemap exists to build our own page list.\n * Returns deduplicated absolute paths, filtered and capped.\n */\nexport function extractAllInternalLinks(html: string, domain: string, limit = 30): string[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const hrefMatches = html.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#') || path.includes('?')) continue;\n\n // Skip resource files and utility paths\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|zip|woff|woff2|ttf|eot|mp4|webm|mp3)$/i.test(path)) continue;\n if (/^\\/(api|wp-admin|wp-includes|wp-json|static|assets|_next|auth|login|signup|sign-up|register|cart|checkout|account|admin|cdn-cgi|feed|rss)\\b/i.test(path)) continue;\n\n // Skip anchors-only and mailto/tel\n if (path.startsWith('mailto:') || path.startsWith('tel:')) continue;\n\n paths.add(path);\n }\n\n // Return up to limit paths, prioritizing shorter paths (more likely to be main pages)\n return Array.from(paths)\n .sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b))\n .slice(0, limit);\n}\n\n// ─── Content page extraction from sitemap ────────────────────────────────────\n\n/**\n * Extract non-blog deep content pages from sitemap XML.\n * Targets service pages, product pages, etc. (not blog/article posts).\n */\nexport function extractContentPagesFromSitemap(\n sitemapText: string,\n domain: string,\n limit = 6\n): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const candidates: string[] = [];\n\n // Paths to skip (already covered by blog sample or common pages)\n const skipPatterns = /\\/(?:blog|articles?|posts?|news|tag|category|author|feed|faq|about|pricing|contact|team|resources?|docs?|documentation|help|support|case-studies|customers|testimonials|sitemap|wp-|api|login|cart|checkout|search)\\b/i;\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n if (skipPatterns.test(path)) continue;\n\n // Want pages with 1-2 path segments (service pages, product pages)\n const segments = path.split('/').filter(Boolean);\n if (segments.length < 1 || segments.length > 3) continue;\n\n candidates.push(url);\n } catch {\n continue;\n }\n }\n\n // Return evenly spaced pages from the list for variety\n if (candidates.length <= limit) return candidates;\n\n const result: string[] = [];\n for (let i = 0; i < limit; i++) {\n const index = Math.round(i * (candidates.length - 1) / (limit - 1));\n result.push(candidates[index]);\n }\n return result;\n}\n\n// ─── Main multi-page fetcher ─────────────────────────────────────────────────\n\nexport interface MultiPageOptions {\n timeoutMs?: number;\n}\n\n/**\n * Fetch additional pages beyond what prefetchSiteData provides.\n * Discovers pages from nav links + common path variants + sitemap content pages.\n * All fetched pages are appended to siteData.blogSample so existing\n * getCombinedHtml() and criteria checks pick them up automatically.\n *\n * Mutates siteData in place and returns the count of new pages added.\n */\nexport async function fetchMultiPageData(\n siteData: SiteData,\n options?: MultiPageOptions\n): Promise<number> {\n if (!siteData.protocol || !siteData.homepage) return 0;\n\n const timeoutMs = options?.timeoutMs ?? 10000;\n const baseUrl = `${siteData.protocol}://${siteData.domain}`;\n const existingUrls = new Set<string>();\n\n // Track already-fetched URLs\n existingUrls.add(baseUrl + '/');\n existingUrls.add(baseUrl);\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n if (page.finalUrl) existingUrls.add(page.finalUrl);\n }\n }\n\n // Collect candidate URLs to fetch (URL -> category)\n const urlsToFetch = new Map<string, PageCategory>();\n\n // Source 1: Nav links from homepage\n const navPaths = extractNavLinks(siteData.homepage.text, siteData.domain);\n\n // Source 2: Common page variants (try nav links first, then fallback paths)\n for (const [category, variants] of Object.entries(PAGE_VARIANTS)) {\n // Check if any nav link matches this category\n const navMatch = navPaths.find(p =>\n variants.some(v => p.toLowerCase() === v || p.toLowerCase().startsWith(v + '/'))\n );\n\n if (navMatch) {\n const url = `${baseUrl}${navMatch}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n } else {\n // Try first variant as fallback\n const url = `${baseUrl}${variants[0]}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n }\n }\n\n // Source 3: Content pages from sitemap\n if (siteData.sitemapXml && siteData.sitemapXml.status === 200) {\n const contentUrls = extractContentPagesFromSitemap(\n siteData.sitemapXml.text,\n siteData.domain,\n 6\n );\n for (const url of contentUrls) {\n if (!existingUrls.has(url)) urlsToFetch.set(url, 'content');\n }\n }\n\n // Source 4: When no sitemap blog sample exists, extract ALL internal links\n // from homepage as a fallback to build our own page list\n const hasBlogSample = (siteData.blogSample?.length ?? 0) > 3;\n if (!hasBlogSample) {\n const allPaths = extractAllInternalLinks(siteData.homepage.text, siteData.domain, 30);\n for (const path of allPaths) {\n const url = `${baseUrl}${path}`;\n if (!existingUrls.has(url) && !urlsToFetch.has(url)) {\n urlsToFetch.set(url, 'content');\n }\n }\n }\n\n // Fetch all URLs in parallel\n const entries = Array.from(urlsToFetch.entries());\n if (entries.length === 0) return 0;\n\n const results = await Promise.all(entries.map(([url]) => fetchPage(url, timeoutMs)));\n\n // Append successful results to blogSample with category tags\n if (!siteData.blogSample) siteData.blogSample = [];\n\n let added = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result && result.text.length > 500) {\n result.category = entries[i][1];\n siteData.blogSample.push(result);\n added++;\n }\n }\n\n return added;\n}\n","/**\n * Per-page AEO scoring.\n * Evaluates 14 of 28 criteria that apply at individual page level.\n * Produces a 0-100 AEO score per page.\n */\n\nimport type { SiteData } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PageCriterionScore {\n criterion: string;\n criterion_label: string;\n score: number;\n weight: number;\n}\n\nexport interface PageScoreResult {\n aeoScore: number;\n criterionScores: PageCriterionScore[];\n}\n\n// ─── Criteria weights (mirrored from scoring.ts for the 14 page-level criteria) ─\n\nconst PAGE_CRITERIA: Record<string, { weight: number; label: string }> = {\n // Content Substance\n original_data: { weight: 0.10, label: 'Original Data & Expert Content' },\n fact_density: { weight: 0.06, label: 'Fact & Data Density' },\n direct_answer_density: { weight: 0.05, label: 'Direct Answer Paragraphs' },\n qa_content_format: { weight: 0.05, label: 'Q&A Content Format' },\n query_answer_alignment: { weight: 0.05, label: 'Query-Answer Alignment' },\n faq_section: { weight: 0.04, label: 'FAQ Section Content' },\n // Content Organization\n content_freshness: { weight: 0.04, label: 'Content Freshness Signals' },\n schema_markup: { weight: 0.03, label: 'Schema.org Structured Data' },\n table_list_extractability: { weight: 0.03, label: 'Table & List Extractability' },\n definition_patterns: { weight: 0.02, label: 'Definition Patterns' },\n visible_date_signal: { weight: 0.02, label: 'Visible Date Signal' },\n semantic_html: { weight: 0.02, label: 'Semantic HTML5 & Accessibility' },\n clean_html: { weight: 0.02, label: 'Clean, Crawlable HTML' },\n // Technical Plumbing\n canonical_url: { weight: 0.01, label: 'Canonical URL Strategy' },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractJsonLdBlocks(html: string): string[] {\n const blocks: string[] = [];\n const regex = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(html)) !== null) {\n blocks.push(match[1]);\n }\n return blocks;\n}\n\nfunction extractTypesFromJsonLd(blocks: string[]): Set<string> {\n const types = new Set<string>();\n for (const block of blocks) {\n const typeMatches = block.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n return types;\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction extractQuestionHeadings(html: string): string[] {\n const headings = html.match(/<h[2-3][^>]*>([\\s\\S]*?)<\\/h[2-3]>/gi) || [];\n const questions: string[] = [];\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questions.push(text);\n }\n }\n return questions;\n}\n\n/** Check if a question heading is followed by an answer paragraph (≥20 chars). */\nfunction countAnsweredQuestions(html: string): { total: number; answered: number } {\n const questions = extractQuestionHeadings(html);\n if (questions.length === 0) return { total: 0, answered: 0 };\n\n let answered = 0;\n for (const q of questions) {\n // Escape the question text for regex matching\n const escaped = q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // Look for the heading containing this text, then a <p> after it\n const pattern = new RegExp(escaped + '[\\\\s\\\\S]*?</h[2-3]>\\\\s*<p[^>]*>([\\\\s\\\\S]*?)</p>', 'i');\n const match = html.match(pattern);\n if (match && match[1].replace(/<[^>]*>/g, '').trim().length >= 20) {\n answered++;\n }\n }\n return { total: questions.length, answered };\n}\n\nfunction cap(value: number, max: number): number {\n return Math.min(value, max);\n}\n\n// ─── 14 Scoring Functions ───────────────────────────────────────────────────\n\n/** 1. Schema.org Structured Data */\nexport function scoreSchemaMarkup(html: string): number {\n const blocks = extractJsonLdBlocks(html);\n if (blocks.length === 0) return 0;\n\n let score = 3; // has JSON-LD\n const types = extractTypesFromJsonLd(blocks);\n\n const knownTypes = ['Organization', 'LocalBusiness', 'Article', 'FAQPage', 'Product', 'WebPage',\n 'BreadcrumbList', 'HowTo', 'Person', 'WebSite', 'BlogPosting', 'Service'];\n let knownCount = 0;\n for (const t of types) {\n if (knownTypes.includes(t)) knownCount++;\n }\n score += cap(knownCount * 2, 4);\n\n if (types.has('Organization') || types.has('LocalBusiness')) score += 2;\n if (types.has('FAQPage')) score += 1;\n\n return cap(score, 10);\n}\n\n/** 2. Q&A Content Format */\nexport function scoreQAFormat(html: string): number {\n const questions = extractQuestionHeadings(html);\n let score = 0;\n\n if (questions.length >= 10) score += 5;\n else if (questions.length >= 3) score += 3;\n else if (questions.length >= 1) score += 1;\n\n // Direct-answer format: question heading followed by paragraph ≥20 chars\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 1) score += 3;\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n return cap(score, 10);\n}\n\n/** 3. Clean, Crawlable HTML */\nexport function scoreCleanHtml(html: string): number {\n let score = 0;\n\n // Semantic elements\n const semantics = ['<main', '<article', '<section'];\n let semCount = 0;\n for (const tag of semantics) {\n if (html.toLowerCase().includes(tag)) semCount++;\n }\n score += cap(semCount, 3);\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n // Text content > 500 chars\n const text = getTextContent(html);\n if (text.length > 500) score += 3;\n\n // Title + meta description\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (hasTitle && hasDesc) score += 2;\n\n return cap(score, 10);\n}\n\n/** 4. FAQ Section Content */\nexport function scoreFaqSection(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // FAQ/frequently-asked text\n if (/frequently\\s*asked|faq/i.test(html)) score += 2;\n\n // FAQPage in JSON-LD\n const blocks = extractJsonLdBlocks(html);\n const types = extractTypesFromJsonLd(blocks);\n if (types.has('FAQPage')) score += 3;\n\n // Question headings ≥ 10\n const questions = extractQuestionHeadings(html);\n if (questions.length >= 10) score += 1;\n\n // Accordion/toggle patterns (details/summary or common CSS classes)\n if (/<details[\\s>]/i.test(html) || /accordion|collapsible|toggle/i.test(lowerHtml)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 5. Original Data & Expert Content */\nexport function scoreOriginalData(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Stats with research context\n if (/\\b(our (study|analysis|research|survey|data|findings))\\b/i.test(text)) {\n score += 3;\n } else if (/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(text)) {\n score += 1;\n }\n\n // Case studies with metrics\n if (/\\bcase\\s+stud(y|ies)\\b/i.test(text) && /\\d+(\\.\\d+)?%|\\$[\\d,.]+/.test(text)) {\n score += 3;\n } else if (/\\bcase\\s+stud(y|ies)\\b/i.test(text)) {\n score += 1;\n }\n\n // Expert attribution\n if (/\\baccording\\s+to\\b|\\bexpert|\\b(Ph\\.?D|MD|professor|analyst|researcher)\\b/i.test(text)) {\n score += 2;\n }\n\n // Blog content (internal links to /blog)\n if (/href=[\"'][^\"']*\\/blog\\b/i.test(html)) {\n score += 2;\n }\n\n return cap(score, 10);\n}\n\n/** 6. Query-Answer Alignment */\nexport function scoreQueryAnswerAlignment(html: string): number {\n const { total, answered } = countAnsweredQuestions(html);\n\n // No question headings = neutral\n if (total === 0) return 5;\n\n const ratio = answered / total;\n if (ratio >= 0.8) return 10;\n if (ratio >= 0.5) return 7;\n if (answered > 0) return 4;\n return 0;\n}\n\n/** 7. Content Freshness Signals */\nexport function scoreContentFreshness(html: string): number {\n let score = 0;\n const blocks = extractJsonLdBlocks(html);\n\n // JSON-LD datePublished/dateModified\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) score += 3;\n else if (timeElements.length === 1) score += 1;\n\n // Article meta tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // Current or previous year reference\n const currentYear = new Date().getFullYear();\n const yearPattern = new RegExp(`\\\\b(${currentYear}|${currentYear - 1})\\\\b`);\n if (yearPattern.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 8. Table & List Extractability */\nexport function scoreTableListExtractability(html: string): number {\n let score = 0;\n\n // Tables with <th>\n const tablesWithHeaders = html.match(/<table[\\s\\S]*?<th[\\s>]/gi) || [];\n if (tablesWithHeaders.length >= 2) score += 4;\n else if (tablesWithHeaders.length === 1) score += 3;\n\n // Tables without headers\n if (tablesWithHeaders.length === 0 && /<table[\\s>]/i.test(html)) score += 1;\n\n // Ordered lists\n if (/<ol[\\s>]/i.test(html)) score += 2;\n\n // Unordered lists\n if (/<ul[\\s>]/i.test(html)) score += 2;\n\n // ≥10 list items\n const listItems = html.match(/<li[\\s>]/gi) || [];\n if (listItems.length >= 10) score += 1;\n\n // Definition lists\n if (/<dl[\\s>]/i.test(html)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 9. Direct Answer Paragraphs */\nexport function scoreDirectAnswerDensity(html: string): number {\n let score = 0;\n\n // Q&A pairs (question heading + <p>)\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 3) score += 6;\n else if (answered >= 1) score += 3;\n\n // Snippet-zone paragraphs (40-150 words)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n let snippetCount = 0;\n for (const p of paragraphs) {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const words = text.split(/\\s+/).filter(w => w.length > 0).length;\n if (words >= 40 && words <= 150) snippetCount++;\n }\n if (snippetCount >= 3) score += 2;\n else if (snippetCount >= 1) score += 1;\n\n // Direct-answer openers\n const directOpeners = getTextContent(html).match(/\\b(yes|no|in short|the answer is|simply put|in summary)\\b/gi) || [];\n if (directOpeners.length >= 2) score += 2;\n\n return cap(score, 10);\n}\n\n/** 10. Semantic HTML5 & Accessibility */\nexport function scoreSemanticHtml(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // 6 semantic elements\n const elements = ['<main', '<article', '<time', '<nav', '<header', '<footer'];\n let count = 0;\n for (const el of elements) {\n if (lowerHtml.includes(el)) count++;\n }\n score += cap(Math.floor(count * 0.7), 4);\n\n // Image alt ratio ≥ 80%\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length > 0) {\n let withAlt = 0;\n for (const img of imgTags) {\n if (/\\salt=[\"'][^\"']*[\"']/i.test(img)) withAlt++;\n }\n if (withAlt / imgTags.length >= 0.8) score += 2;\n }\n\n // lang attribute\n if (/<html[^>]*\\slang=[\"'][^\"']+[\"']/i.test(html)) score += 2;\n\n // ARIA attributes\n if (/\\baria-/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 11. Fact & Data Density */\nexport function scoreFactDensity(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Numeric data points (%, $, count phrases)\n const numericPatterns = text.match(/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b|\\b\\d+\\s*(million|billion|thousand|users|customers|employees)\\b/gi) || [];\n if (numericPatterns.length >= 6) score += 5;\n else if (numericPatterns.length >= 3) score += 3;\n else if (numericPatterns.length >= 1) score += 1;\n\n // Year references\n const years = new Set<string>();\n const yearMatches = text.match(/\\b(19|20)\\d{2}\\b/g) || [];\n for (const y of yearMatches) years.add(y);\n if (years.size >= 2) score += 2;\n else if (years.size === 1) score += 1;\n\n // Attribution phrases\n if (/\\baccording to\\b|\\bsource:\\s|\\bcited\\b|\\breported by\\b/i.test(text)) score += 2;\n\n // Units (kg, miles, hours, etc.)\n const units = text.match(/\\b\\d+\\s*(kg|lb|miles|km|hours|minutes|days|months|years|GB|MB|TB)\\b/gi) || [];\n if (units.length >= 2) score += 1;\n\n return cap(score, 10);\n}\n\n/** 12. Definition Patterns */\nexport function scoreDefinitionPatterns(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition phrases\n const defPatterns = text.match(/\\b(is a|is an|refers to|defined as|means that|also known as|abbreviated as)\\b/gi) || [];\n if (defPatterns.length >= 3) score += 5;\n else if (defPatterns.length >= 1) score += 3;\n\n // Early placement (first 2000 chars)\n const early = text.slice(0, 2000);\n if (/\\b(is a|is an|refers to|defined as)\\b/i.test(early)) score += 2;\n\n // <dfn> or <abbr>\n if (/<dfn[\\s>]/i.test(html) || /<abbr[\\s>]/i.test(html)) score += 1;\n\n // <dl> or glossary\n if (/<dl[\\s>]/i.test(html) || /glossary/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 13. Canonical URL Strategy */\nexport function scoreCanonicalUrl(html: string, url?: string): number {\n let score = 0;\n\n // Has canonical link\n const canonicalMatch = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i)\n || html.match(/<link[^>]*href=[\"']([^\"']+)[\"'][^>]*rel=[\"']canonical[\"']/i);\n\n if (!canonicalMatch) return 0;\n score += 4;\n\n const canonicalHref = canonicalMatch[1];\n\n // Self-referencing\n if (url) {\n try {\n const canonicalUrl = new URL(canonicalHref, url);\n const pageUrl = new URL(url);\n if (canonicalUrl.pathname === pageUrl.pathname && canonicalUrl.hostname === pageUrl.hostname) {\n score += 3;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n\n // HTTPS\n if (canonicalHref.startsWith('https://')) score += 2;\n\n // Single canonical (no duplicates)\n const allCanonicals = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*>/gi) || [];\n if (allCanonicals.length === 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 14. Visible Date Signal */\nexport function scoreVisibleDateSignal(html: string): number {\n let score = 0;\n\n // <time datetime> with visible text\n const timeWithDatetime = html.match(/<time[^>]*datetime=[\"'][^\"']+[\"'][^>]*>[^<]+<\\/time>/gi) || [];\n if (timeWithDatetime.length > 0) score += 5;\n\n // JSON-LD datePublished/dateModified\n const blocks = extractJsonLdBlocks(html);\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // Article meta date tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // dateModified within 180 days\n const modifiedMatch = allJsonLd.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (modifiedMatch) {\n try {\n const modified = new Date(modifiedMatch[1]);\n const daysDiff = (Date.now() - modified.getTime()) / (1000 * 60 * 60 * 24);\n if (daysDiff <= 180) score += 1;\n } catch {\n // Ignore date parse errors\n }\n }\n\n return cap(score, 10);\n}\n\n// ─── Scoring function map ───────────────────────────────────────────────────\n\nconst SCORING_FUNCTIONS: Record<string, (html: string, url?: string) => number> = {\n schema_markup: scoreSchemaMarkup,\n qa_content_format: scoreQAFormat,\n clean_html: scoreCleanHtml,\n faq_section: scoreFaqSection,\n original_data: scoreOriginalData,\n query_answer_alignment: scoreQueryAnswerAlignment,\n content_freshness: scoreContentFreshness,\n table_list_extractability: scoreTableListExtractability,\n direct_answer_density: scoreDirectAnswerDensity,\n semantic_html: scoreSemanticHtml,\n fact_density: scoreFactDensity,\n definition_patterns: scoreDefinitionPatterns,\n canonical_url: scoreCanonicalUrl,\n visible_date_signal: scoreVisibleDateSignal,\n};\n\n// ─── Main API ───────────────────────────────────────────────────────────────\n\n/**\n * Score a single page against 14 AEO criteria.\n * Returns a 0-100 AEO score and individual criterion scores.\n */\nexport function scorePage(html: string, url?: string): PageScoreResult {\n let totalWeight = 0;\n let weightedSum = 0;\n const criterionScores: PageCriterionScore[] = [];\n\n for (const [criterion, { weight, label }] of Object.entries(PAGE_CRITERIA)) {\n const fn = SCORING_FUNCTIONS[criterion]!;\n const score = fn(html, url);\n\n criterionScores.push({ criterion, criterion_label: label, score, weight });\n weightedSum += (score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n const aeoScore = totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n\n return { aeoScore, criterionScores };\n}\n\n/**\n * Score all crawled pages (homepage + blogSample).\n */\nexport function scoreAllPages(siteData: SiteData): PageScoreResult[] {\n const results: PageScoreResult[] = [];\n\n if (siteData.homepage) {\n const url = siteData.protocol ? `${siteData.protocol}://${siteData.domain}` : undefined;\n results.push(scorePage(siteData.homepage.text, url));\n }\n\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || undefined;\n results.push(scorePage(page.text, url));\n }\n }\n\n return results;\n}\n","/**\n * Per-page analysis for instant audit.\n * Runs 12 deterministic checks on each crawled page (no LLM).\n */\n\nimport type { PageCategory, SiteData } from './site-crawler.js';\nimport type { PageIssue, PageReview } from './types.js';\nimport { scorePage } from './page-scorer.js';\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Individual checks ──────────────────────────────────────────────────────\n\nfunction checkMissingTitle(html: string): PageIssue | null {\n const hasTitle = /<title[^>]*>[\\s\\S]*?<\\/title>/i.test(html);\n if (!hasTitle) {\n return { check: 'missing-title', label: 'Missing <title> tag', severity: 'error' };\n }\n const title = extractTitle(html);\n if (!title) {\n return { check: 'missing-title', label: 'Empty <title> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMissingMetaDescription(html: string): PageIssue | null {\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (!hasDesc) {\n return { check: 'missing-meta-description', label: 'Missing meta description', severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (!h1Matches || h1Matches.length === 0) {\n return { check: 'no-h1', label: 'No <h1> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMultipleH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (h1Matches && h1Matches.length > 1) {\n return { check: 'multiple-h1', label: `Multiple <h1> tags (${h1Matches.length})`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkNoSchema(html: string): PageIssue | null {\n const hasLdJson = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>/i.test(html);\n if (!hasLdJson) {\n return { check: 'no-schema', label: 'No JSON-LD structured data', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingCanonical(html: string): PageIssue | null {\n const hasCanonical = /<link[^>]*rel=[\"']canonical[\"'][^>]*>/i.test(html);\n if (!hasCanonical) {\n return { check: 'missing-canonical', label: 'Missing canonical link', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingOgTags(html: string): PageIssue | null {\n const hasOg = /<meta\\s[^>]*property=[\"']og:/i.test(html);\n if (!hasOg) {\n return { check: 'missing-og-tags', label: 'No Open Graph tags', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkThinContent(wordCount: number): PageIssue | null {\n if (wordCount < 300) {\n return { check: 'thin-content', label: `Thin content (${wordCount} words)`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkImagesMissingAlt(html: string): PageIssue | null {\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length === 0) return null;\n\n let missingAlt = 0;\n for (const img of imgTags) {\n const hasAlt = /\\salt=[\"'][^\"']+[\"']/i.test(img);\n const hasEmptyAlt = /\\salt=[\"'][\"']/i.test(img); // decorative\n if (!hasAlt && !hasEmptyAlt) missingAlt++;\n }\n\n if (missingAlt > 0) {\n return {\n check: 'images-missing-alt',\n label: `${missingAlt} image${missingAlt > 1 ? 's' : ''} missing alt text`,\n severity: 'warning',\n };\n }\n return null;\n}\n\nfunction checkNoInternalLinks(html: string, url: string): PageIssue | null {\n let domain: string;\n try {\n domain = new URL(url).hostname.replace(/^www\\./, '').toLowerCase();\n } catch {\n return null;\n }\n\n const links = html.match(/<a\\s[^>]*href=[\"']([^\"'#]*?)[\"'][^>]*>/gi) || [];\n let internalCount = 0;\n\n for (const link of links) {\n const hrefMatch = link.match(/href=[\"']([^\"'#]*?)[\"']/i);\n if (!hrefMatch) continue;\n const href = hrefMatch[1];\n\n if (href.startsWith('/') && !href.startsWith('//')) {\n internalCount++;\n } else if (href.startsWith('http')) {\n try {\n const linkDomain = new URL(href).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain === domain) internalCount++;\n } catch {\n // skip\n }\n }\n }\n\n if (internalCount === 0) {\n return { check: 'no-internal-links', label: 'No internal links found', severity: 'warning' };\n }\n return null;\n}\n\n// ─── Strength checks ────────────────────────────────────────────────────────\n\nfunction checkHasStructuredData(html: string): PageIssue | null {\n const ldBlocks = html.match(/<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (ldBlocks.length === 0) return null;\n\n const types = new Set<string>();\n for (const block of ldBlocks) {\n const content = block.replace(/<\\/?script[^>]*>/gi, '');\n const typeMatches = content.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n\n if (types.size > 0) {\n return {\n check: 'has-structured-data',\n label: `JSON-LD: ${Array.from(types).join(', ')}`,\n severity: 'info',\n };\n }\n return null;\n}\n\nfunction checkHasQuestionHeadings(html: string): PageIssue | null {\n const headings = html.match(/<h[2-4][^>]*>[\\s\\S]*?<\\/h[2-4]>/gi) || [];\n let questionCount = 0;\n\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questionCount++;\n }\n }\n\n if (questionCount > 0) {\n return {\n check: 'has-question-headings',\n label: `${questionCount} question-format heading${questionCount > 1 ? 's' : ''}`,\n severity: 'info',\n };\n }\n return null;\n}\n\n// ─── Main analyzers ─────────────────────────────────────────────────────────\n\nexport function analyzePage(html: string, url: string, category: PageCategory): PageReview {\n const title = extractTitle(html);\n const textContent = getTextContent(html);\n const wordCount = countWords(textContent);\n\n const issues: PageIssue[] = [];\n const strengths: PageIssue[] = [];\n\n // Issue checks\n const issueChecks = [\n checkMissingTitle(html),\n checkMissingMetaDescription(html),\n checkNoH1(html),\n checkMultipleH1(html),\n checkNoSchema(html),\n checkMissingCanonical(html),\n checkMissingOgTags(html),\n checkThinContent(wordCount),\n checkImagesMissingAlt(html),\n checkNoInternalLinks(html, url),\n ];\n\n for (const result of issueChecks) {\n if (result) issues.push(result);\n }\n\n // Strength checks\n const strengthChecks = [\n checkHasStructuredData(html),\n checkHasQuestionHeadings(html),\n ];\n\n for (const result of strengthChecks) {\n if (result) strengths.push(result);\n }\n\n const { aeoScore, criterionScores } = scorePage(html, url);\n return { url, title, category, wordCount, issues, strengths, aeoScore, criterionScores };\n}\n\nexport function analyzeAllPages(siteData: SiteData): PageReview[] {\n const reviews: PageReview[] = [];\n\n // Analyze homepage\n if (siteData.homepage) {\n const url = `${siteData.protocol}://${siteData.domain}`;\n reviews.push(analyzePage(siteData.homepage.text, url, siteData.homepage.category || 'homepage'));\n }\n\n // Analyze all pages in blogSample\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || 'unknown';\n reviews.push(analyzePage(page.text, url, page.category || 'content'));\n }\n }\n\n return reviews;\n}\n","/**\n * Programmatic audit API.\n * Runs the full 7-phase AEO audit pipeline and returns structured results.\n */\n\nimport { prefetchSiteData, auditSiteFromData, extractRawDataSummary } from './site-crawler.js';\nimport { calculateOverallScore } from './scoring.js';\nimport { isSpaShell, fetchWithHeadless, classifyRendering } from './headless-fetch.js';\nimport { buildScorecard, buildDetailedFindings } from './scorecard-builder.js';\nimport { generateVerdict, generateOpportunities, generatePitchNumbers, generateBottomLine } from './narrative-generator.js';\nimport { fetchMultiPageData } from './multi-page-fetcher.js';\nimport { analyzeAllPages } from './page-analyzer.js';\nimport type { AuditData } from './types.js';\n\nexport interface AuditOptions {\n /** Skip Puppeteer SPA rendering (default: false) */\n noHeadless?: boolean;\n /** Homepage + blog only, skip extra page discovery (default: false) */\n noMultiPage?: boolean;\n /** Fetch timeout in ms (default: 15000) */\n timeout?: number;\n /** Enable full-site BFS crawl (default: false) */\n fullCrawl?: boolean;\n /** Max pages for full crawl (default: 200) */\n maxPages?: number;\n /** Parallel fetch concurrency for full crawl (default: 5) */\n concurrency?: number;\n}\n\nexport interface AuditResult extends AuditData {\n /** True if headless browser was used for SPA rendering */\n renderedWithHeadless?: boolean;\n /** Wall-clock seconds */\n elapsed: number;\n}\n\nfunction getTextLength(html: string): number {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim().length;\n}\n\n/**\n * Run a complete AEO audit on a domain.\n *\n * @example\n * ```ts\n * import { audit } from 'aeorank';\n * const result = await audit('example.com');\n * console.log(result.overallScore); // 0-100\n * ```\n */\nexport async function audit(domain: string, options?: AuditOptions): Promise<AuditResult> {\n const startTime = Date.now();\n let renderedWithHeadless = false;\n\n // Phase 1: Fetch site data\n const siteData = await prefetchSiteData(domain);\n\n if (!siteData.protocol) {\n throw new Error(`Could not connect to ${domain} (no HTTPS or HTTP response)`);\n }\n\n if (siteData.redirectedTo) {\n throw new Error(`${domain} redirects to ${siteData.redirectedTo} (hijacked domain)`);\n }\n\n if (siteData.parkedReason) {\n throw new Error(`${domain} is a parked/lost domain (${siteData.parkedReason})`);\n }\n\n // Phase 2: SPA detection + headless rendering\n if (!options?.noHeadless && siteData.homepage && isSpaShell(siteData.homepage.text)) {\n const rawTextLen = getTextLength(siteData.homepage.text);\n const url = `${siteData.protocol}://${domain}`;\n const rendered = await fetchWithHeadless(url);\n\n if (rendered) {\n const renderedTextLen = getTextLength(rendered.text);\n if (renderedTextLen > rawTextLen) {\n siteData.homepage = rendered;\n renderedWithHeadless = true;\n }\n }\n\n if (renderedWithHeadless && siteData.faqPage && isSpaShell(siteData.faqPage.text)) {\n const faqUrl = `${siteData.protocol}://${domain}/faq`;\n const renderedFaq = await fetchWithHeadless(faqUrl);\n if (renderedFaq && getTextLength(renderedFaq.text) > getTextLength(siteData.faqPage.text)) {\n siteData.faqPage = renderedFaq;\n }\n }\n }\n\n // Phase 3: Multi-page discovery\n if (options?.fullCrawl) {\n const { crawlFullSite } = await import('./full-site-crawler.js');\n const crawlResult = await crawlFullSite(siteData, {\n maxPages: options.maxPages ?? 200,\n concurrency: options.concurrency ?? 5,\n });\n siteData.blogSample = crawlResult.pages;\n siteData.crawlStats = {\n discovered: crawlResult.discoveredUrls.length,\n fetched: crawlResult.fetchedUrls.length,\n skipped: crawlResult.skippedUrls.length,\n elapsed: crawlResult.elapsed,\n };\n } else if (!options?.noMultiPage) {\n await fetchMultiPageData(siteData);\n }\n\n // Phase 4: Score all 26 criteria\n const results = auditSiteFromData(siteData);\n const overallScore = calculateOverallScore(results);\n const rawData = extractRawDataSummary(siteData);\n if (renderedWithHeadless) rawData.rendered_with_headless = true;\n\n // Phase 5: Build scorecard + detailed findings\n const scorecard = buildScorecard(results);\n const detailedFindings = buildDetailedFindings(results);\n\n // Phase 6: Generate narrative\n const verdict = generateVerdict(overallScore, scorecard, rawData, domain);\n const opportunities = generateOpportunities(scorecard, results);\n const pitchNumbers = generatePitchNumbers(overallScore, rawData, scorecard);\n const bottomLine = generateBottomLine(overallScore, opportunities, scorecard, domain);\n\n // Phase 7: Per-page analysis\n const pagesReviewed = analyzeAllPages(siteData);\n\n const elapsed = Math.round((Date.now() - startTime) / 100) / 10;\n\n return {\n site: domain,\n auditDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),\n auditor: 'AEORank',\n engine: 'instant',\n overallScore,\n verdict,\n scorecard,\n detailedFindings,\n opportunities,\n pitchNumbers,\n bottomLine,\n pagesReviewed,\n elapsed,\n ...(renderedWithHeadless && { renderedWithHeadless: true }),\n };\n}\n","/**\n * Link graph analysis for full-site AEO audits.\n * Builds an internal link graph, detects orphan pages, pillar pages,\n * hub pages, and topic clusters from crawled page data.\n */\n\nimport type { FetchResult, PageCategory } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface LinkEdge {\n source: string;\n target: string;\n anchorText: string;\n}\n\nexport interface PageNode {\n url: string;\n title: string;\n wordCount: number;\n category: PageCategory;\n inDegree: number;\n outDegree: number;\n depth: number;\n isPillar: boolean;\n isHub: boolean;\n isOrphan: boolean;\n}\n\nexport interface TopicCluster {\n pillarUrl: string;\n pillarTitle: string;\n spokes: string[];\n cohesion: number;\n}\n\nexport interface LinkGraphStats {\n totalPages: number;\n totalEdges: number;\n orphanPages: number;\n pillarPages: number;\n hubPages: number;\n avgDepth: number;\n maxDepth: number;\n clusters: number;\n}\n\nexport interface LinkGraph {\n nodes: Map<string, PageNode>;\n edges: LinkEdge[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\n// ─── Serialization (Map -> plain object for JSON) ───────────────────────────\n\nexport interface SerializedLinkGraph {\n nodes: PageNode[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\nexport function serializeLinkGraph(graph: LinkGraph): SerializedLinkGraph {\n return {\n nodes: Array.from(graph.nodes.values()),\n stats: graph.stats,\n clusters: graph.clusters,\n };\n}\n\n// ─── URL normalization (matches full-site-crawler.ts:421-429) ───────────────\n\nfunction normalizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return (parsed.origin + parsed.pathname.replace(/\\/+$/, '') + parsed.search).toLowerCase();\n } catch {\n return url.toLowerCase();\n }\n}\n\n// ─── Resource / skip-path patterns (same as full-site-crawler.ts) ───────────\n\nconst RESOURCE_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|woff|woff2|ttf|eot|mp4|mp3|webp|avif|zip|gz|tar|json)$/i;\n\nconst SKIP_PATH_PATTERNS = /^\\/(api|wp-admin|wp-json|static|assets|_next|auth|login|signup|cart|checkout|admin|feed|xmlrpc)\\b/i;\n\n// ─── HTML helpers (mirrors page-analyzer.ts) ────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Link extraction with anchor text ───────────────────────────────────────\n\n/**\n * Extract internal links from HTML with their anchor text.\n * Similar to extractInternalLinks in full-site-crawler.ts but returns LinkEdge[].\n */\nexport function extractLinksWithAnchors(\n html: string,\n sourceUrl: string,\n domain: string,\n): LinkEdge[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const edges: LinkEdge[] = [];\n const seen = new Set<string>();\n\n // Match <a ...>...</a> tags to get both href and anchor text\n const anchorRegex = /<a\\s[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = anchorRegex.exec(html)) !== null) {\n const href = match[1];\n const rawAnchor = match[2];\n\n if (!href || !href.trim()) continue;\n\n let fullUrl: string;\n\n if (href.startsWith('//')) {\n fullUrl = `https:${href}`;\n } else if (href.startsWith('/')) {\n if (href === '/' || href.startsWith('/#')) continue;\n fullUrl = `https://${domain}${href}`;\n } else if (href.startsWith('http')) {\n fullUrl = href;\n } else if (\n href.startsWith('#') || href.startsWith('?') ||\n href.startsWith('mailto:') || href.startsWith('tel:') ||\n href.startsWith('javascript:')\n ) {\n continue;\n } else {\n fullUrl = `https://${domain}/${href}`;\n }\n\n try {\n const parsed = new URL(fullUrl);\n const linkDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n\n parsed.hash = '';\n const path = parsed.pathname;\n\n if (path === '/' || path === '') continue;\n if (RESOURCE_EXTENSIONS.test(path)) continue;\n if (SKIP_PATH_PATTERNS.test(path)) continue;\n\n const normalized = normalizeUrl(fullUrl);\n const sourceNorm = normalizeUrl(sourceUrl);\n\n // Skip self-links\n if (normalized === sourceNorm) continue;\n\n // Deduplicate edges from same source to same target\n const edgeKey = `${sourceNorm}->${normalized}`;\n if (seen.has(edgeKey)) continue;\n seen.add(edgeKey);\n\n // Strip HTML tags from anchor text\n const anchorText = rawAnchor.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n\n edges.push({\n source: sourceNorm,\n target: normalized,\n anchorText,\n });\n } catch {\n continue;\n }\n }\n\n return edges;\n}\n\n// ─── BFS depth calculation ──────────────────────────────────────────────────\n\n/**\n * BFS from homepage to compute click depth for each node.\n * Unreachable pages get Infinity.\n */\nexport function calculateDepths(\n nodes: Map<string, PageNode>,\n adjacency: Map<string, Set<string>>,\n homepageUrl: string,\n): void {\n const homeNorm = normalizeUrl(homepageUrl);\n\n // Set all to Infinity first\n for (const node of nodes.values()) {\n node.depth = Infinity;\n }\n\n // Homepage gets depth 0\n const homeNode = nodes.get(homeNorm);\n if (homeNode) {\n homeNode.depth = 0;\n }\n\n // BFS\n const queue: string[] = [homeNorm];\n const visited = new Set<string>([homeNorm]);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentNode = nodes.get(current);\n if (!currentNode) continue;\n const nextDepth = currentNode.depth + 1;\n\n const neighbors = adjacency.get(current);\n if (!neighbors) continue;\n\n for (const neighbor of neighbors) {\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n const neighborNode = nodes.get(neighbor);\n if (neighborNode) {\n neighborNode.depth = nextDepth;\n queue.push(neighbor);\n }\n }\n }\n}\n\n// ─── Pillar detection ───────────────────────────────────────────────────────\n\nconst PILLAR_CATEGORIES: Set<PageCategory> = new Set(['blog', 'content', 'resources', 'docs']);\n\n/**\n * Detect pillar pages: long content with significant in/out linking.\n * wordCount >= 1500 AND inDegree >= 3 AND outDegree >= 3\n * AND category in blog/content/resources/docs AND not homepage (depth > 0).\n */\nexport function detectPillars(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isPillar =\n node.wordCount >= 1500 &&\n node.inDegree >= 3 &&\n node.outDegree >= 3 &&\n PILLAR_CATEGORIES.has(node.category) &&\n node.depth > 0;\n }\n}\n\n// ─── Hub detection ──────────────────────────────────────────────────────────\n\nconst HUB_CATEGORIES: Set<PageCategory> = new Set(['homepage', 'resources', 'docs']);\n\n/**\n * Detect hub pages: high outDegree navigation/index pages.\n * outDegree >= 10 with category homepage/resources/docs, OR outDegree >= 15.\n */\nexport function detectHubs(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isHub =\n (node.outDegree >= 10 && HUB_CATEGORIES.has(node.category)) ||\n node.outDegree >= 15;\n }\n}\n\n// ─── Cluster detection ──────────────────────────────────────────────────────\n\n/**\n * Detect topic clusters around pillar pages.\n * For each pillar, gather spokes (pages linked to/from pillar).\n * Cohesion = actual edges between cluster members / possible edges.\n * Minimum 2 spokes to form a cluster.\n */\nexport function detectClusters(\n nodes: Map<string, PageNode>,\n edges: LinkEdge[],\n): TopicCluster[] {\n const clusters: TopicCluster[] = [];\n\n // Build quick lookup for edges\n const edgeSet = new Set<string>();\n for (const edge of edges) {\n edgeSet.add(`${edge.source}->${edge.target}`);\n }\n\n for (const node of nodes.values()) {\n if (!node.isPillar) continue;\n\n const pillarNorm = normalizeUrl(node.url);\n const spokeSet = new Set<string>();\n\n // Find spokes: pages linked to/from this pillar\n for (const edge of edges) {\n if (edge.source === pillarNorm && nodes.has(edge.target)) {\n spokeSet.add(edge.target);\n }\n if (edge.target === pillarNorm && nodes.has(edge.source)) {\n spokeSet.add(edge.source);\n }\n }\n\n // Remove the pillar itself from spokes\n spokeSet.delete(pillarNorm);\n\n if (spokeSet.size < 2) continue;\n\n const spokes = Array.from(spokeSet);\n\n // Compute cohesion: edges between cluster members (pillar + spokes)\n const members = [pillarNorm, ...spokes];\n let actualEdges = 0;\n const possibleEdges = members.length * (members.length - 1); // directed\n\n for (const from of members) {\n for (const to of members) {\n if (from === to) continue;\n if (edgeSet.has(`${from}->${to}`)) {\n actualEdges++;\n }\n }\n }\n\n const cohesion = possibleEdges > 0 ? Math.round((actualEdges / possibleEdges) * 100) : 0;\n\n clusters.push({\n pillarUrl: node.url,\n pillarTitle: node.title,\n spokes,\n cohesion,\n });\n }\n\n return clusters;\n}\n\n// ─── Main graph builder ─────────────────────────────────────────────────────\n\n/**\n * Build a link graph from crawled pages.\n * @param pages - Array of FetchResult from full-site crawl\n * @param domain - The site domain\n * @param homepageUrl - Full URL of the homepage (e.g. https://example.com)\n */\nexport function buildLinkGraph(\n pages: FetchResult[],\n domain: string,\n homepageUrl: string,\n): LinkGraph {\n const nodes = new Map<string, PageNode>();\n const allEdges: LinkEdge[] = [];\n const adjacency = new Map<string, Set<string>>();\n const inDegreeMap = new Map<string, number>();\n\n // Phase 1: Create nodes from all pages\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const norm = normalizeUrl(url);\n\n if (nodes.has(norm)) continue;\n\n const title = extractTitle(page.text);\n const text = getTextContent(page.text);\n const wordCount = countWords(text);\n\n nodes.set(norm, {\n url: norm,\n title,\n wordCount,\n category: page.category || 'content',\n inDegree: 0,\n outDegree: 0,\n depth: Infinity,\n isPillar: false,\n isHub: false,\n isOrphan: false,\n });\n }\n\n // Phase 2: Extract edges\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const sourceNorm = normalizeUrl(url);\n\n const edges = extractLinksWithAnchors(page.text, url, domain);\n\n for (const edge of edges) {\n // Normalize edge source/target to match node keys\n const targetNorm = normalizeUrl(edge.target);\n\n // Only count edges to known pages\n if (!nodes.has(targetNorm)) continue;\n\n allEdges.push({\n source: sourceNorm,\n target: targetNorm,\n anchorText: edge.anchorText,\n });\n\n // Build adjacency\n if (!adjacency.has(sourceNorm)) {\n adjacency.set(sourceNorm, new Set());\n }\n adjacency.get(sourceNorm)!.add(targetNorm);\n\n // Track in-degree\n inDegreeMap.set(targetNorm, (inDegreeMap.get(targetNorm) || 0) + 1);\n }\n }\n\n // Phase 3: Set degrees\n for (const [url, node] of nodes) {\n node.inDegree = inDegreeMap.get(url) || 0;\n node.outDegree = adjacency.get(url)?.size || 0;\n }\n\n // Phase 4: BFS depths\n calculateDepths(nodes, adjacency, homepageUrl);\n\n // Phase 5: Detect pillars and hubs\n detectPillars(nodes);\n detectHubs(nodes);\n\n // Phase 6: Mark orphans (zero inbound links, not homepage)\n const homeNorm = normalizeUrl(homepageUrl);\n for (const [url, node] of nodes) {\n node.isOrphan = node.inDegree === 0 && url !== homeNorm;\n }\n\n // Phase 7: Detect clusters\n const clusters = detectClusters(nodes, allEdges);\n\n // Phase 8: Compute stats\n const depthValues = Array.from(nodes.values())\n .map(n => n.depth)\n .filter(d => d !== Infinity);\n const avgDepth = depthValues.length > 0\n ? Math.round((depthValues.reduce((s, d) => s + d, 0) / depthValues.length) * 10) / 10\n : 0;\n const maxDepth = depthValues.length > 0 ? Math.max(...depthValues) : 0;\n\n const stats: LinkGraphStats = {\n totalPages: nodes.size,\n totalEdges: allEdges.length,\n orphanPages: Array.from(nodes.values()).filter(n => n.isOrphan).length,\n pillarPages: Array.from(nodes.values()).filter(n => n.isPillar).length,\n hubPages: Array.from(nodes.values()).filter(n => n.isHub).length,\n avgDepth,\n maxDepth,\n clusters: clusters.length,\n };\n\n return { nodes, edges: allEdges, stats, clusters };\n}\n","/**\n * Fix Plan Engine - generates actionable, phased fix plans from audit scores.\n * Runs alongside the existing opportunities system (no breaking changes).\n * Uses criterion scores, optional per-page data, and optional link graph\n * to produce structured fix plans with code examples and dependency ordering.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { PageReview, PageCriterionScore } from './types.js';\nimport type { LinkGraph } from './link-graph.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface FixAction {\n id: string;\n criterion: string;\n criterionId: string;\n title: string;\n description: string;\n impact: 'critical' | 'high' | 'medium' | 'low';\n effort: 'trivial' | 'low' | 'medium' | 'high';\n impactScore: number;\n category: 'content' | 'structure' | 'discovery' | 'trust';\n steps: string[];\n codeExample?: string;\n successCriteria: string;\n dependsOn?: string[];\n affectedPages?: string[];\n pageCount?: number;\n}\n\nexport interface FixPhase {\n phase: number;\n title: string;\n description: string;\n fixes: FixAction[];\n estimatedImpact: number;\n}\n\nexport interface FixPlanSummary {\n criticalCount: number;\n highCount: number;\n mediumCount: number;\n lowCount: number;\n quickWinCount: number;\n topOpportunity: string;\n estimatedTotalEffort: string;\n}\n\nexport interface FixPlan {\n domain: string;\n generatedAt: string;\n overallScore: number;\n projectedScore: number;\n totalFixes: number;\n phases: FixPhase[];\n quickWins: FixAction[];\n summary: FixPlanSummary;\n}\n\n// ─── Criterion weights (mirrored from scoring.ts) ────────────────────────────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.05,\n query_answer_alignment: 0.05,\n faq_section: 0.04,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n definition_patterns: 0.02,\n visible_date_signal: 0.02,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.02,\n robots_txt: 0.02,\n content_velocity: 0.02,\n content_licensing: 0.02,\n sitemap_completeness: 0.01,\n canonical_url: 0.01,\n rss_feed: 0.01,\n schema_coverage: 0.01,\n speakable_schema: 0.01,\n};\n\n// ─── Phase configuration ────────────────────────────────────────────────────\n\nconst PHASE_CONFIG: { phase: number; title: string; description: string; criteria: string[] }[] = [\n {\n phase: 1,\n title: 'Foundation',\n description: 'Discovery and structural fixes that enable AI crawlers to access and parse your content.',\n criteria: ['robots_txt', 'llms_txt', 'canonical_url', 'clean_html', 'sitemap_completeness'],\n },\n {\n phase: 2,\n title: 'Content',\n description: 'Content quality and format improvements that make your pages citable by AI engines.',\n criteria: [\n 'qa_content_format', 'faq_section', 'original_data', 'definition_patterns',\n 'direct_answer_density', 'fact_density', 'content_freshness',\n 'table_list_extractability', 'query_answer_alignment', 'visible_date_signal',\n 'topic_coherence', 'content_depth',\n ],\n },\n {\n phase: 3,\n title: 'Authority',\n description: 'Trust signals, schema depth, and semantic structure that establish credibility with AI engines.',\n criteria: [\n 'schema_markup', 'schema_coverage', 'speakable_schema', 'author_schema_depth',\n 'content_licensing', 'entity_consistency', 'semantic_html',\n ],\n },\n {\n phase: 4,\n title: 'Architecture',\n description: 'Site architecture, linking patterns, and publishing cadence that support long-term AI visibility.',\n criteria: ['internal_linking', 'content_velocity', 'content_cannibalization', 'rss_feed'],\n },\n];\n\n// ─── Criterion → category mapping ───────────────────────────────────────────\n\nconst CRITERION_CATEGORY: Record<string, FixAction['category']> = {\n robots_txt: 'discovery', llms_txt: 'discovery', canonical_url: 'structure',\n clean_html: 'structure', sitemap_completeness: 'discovery',\n qa_content_format: 'content', faq_section: 'content', original_data: 'content',\n definition_patterns: 'content', direct_answer_density: 'content', fact_density: 'content',\n content_freshness: 'content', table_list_extractability: 'content',\n query_answer_alignment: 'content', visible_date_signal: 'content',\n schema_markup: 'trust', schema_coverage: 'trust', speakable_schema: 'trust',\n author_schema_depth: 'trust', content_licensing: 'trust', entity_consistency: 'trust',\n semantic_html: 'structure', internal_linking: 'structure', content_velocity: 'content',\n content_cannibalization: 'content', rss_feed: 'discovery',\n topic_coherence: 'content', content_depth: 'content',\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction impactFromScore(score: number): FixAction['impact'] {\n if (score <= 3) return 'critical';\n if (score <= 5) return 'high';\n if (score <= 7) return 'medium';\n return 'low';\n}\n\nfunction effortForCriterion(criterion: string, score: number): FixAction['effort'] {\n // Low-effort criteria (file creation, config changes)\n const trivialCriteria = ['llms_txt', 'robots_txt', 'canonical_url', 'content_licensing', 'visible_date_signal'];\n const lowCriteria = ['rss_feed', 'sitemap_completeness', 'speakable_schema', 'author_schema_depth', 'semantic_html', 'definition_patterns', 'content_freshness'];\n const highCriteria = ['original_data', 'content_velocity', 'content_cannibalization'];\n\n if (trivialCriteria.includes(criterion)) return score <= 3 ? 'low' : 'trivial';\n if (lowCriteria.includes(criterion)) return score <= 3 ? 'medium' : 'low';\n if (highCriteria.includes(criterion)) return score <= 5 ? 'high' : 'medium';\n return score <= 3 ? 'medium' : 'low';\n}\n\nfunction getAffectedPages(\n criterion: string,\n pages?: PageReview[],\n threshold = 7,\n): string[] | undefined {\n if (!pages || pages.length === 0) return undefined;\n const affected = pages.filter(p => {\n const cs = p.criterionScores?.find((c: PageCriterionScore) => c.criterion === criterion);\n return cs && cs.score < threshold;\n });\n if (affected.length === 0) return undefined;\n return affected.map(p => p.url);\n}\n\nfunction effortToHours(effort: FixAction['effort']): number {\n switch (effort) {\n case 'trivial': return 0.5;\n case 'low': return 1;\n case 'medium': return 3;\n case 'high': return 8;\n }\n}\n\n// ─── Fix generator type ─────────────────────────────────────────────────────\n\ntype FixGenerator = (\n criterion: CriterionResult,\n pages?: PageReview[],\n linkGraph?: LinkGraph,\n) => FixAction[];\n\n// ─── 26 Fix Generators ─────────────────────────────────────────────────────\n\nconst FIX_GENERATORS: Record<string, FixGenerator> = {\n\n llms_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('llms_txt', c.score);\n const fixes: FixAction[] = [];\n if (c.score <= 6) {\n fixes.push({\n id: 'fix-llms-txt-create',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create /llms.txt file',\n description: 'Add a machine-readable llms.txt file at your domain root that describes your site, services, and key pages for AI engines.',\n impact, effort,\n impactScore: 0, // calculated later\n category: 'discovery',\n steps: [\n 'Create a file named llms.txt in your site root',\n 'Add site name, description, and core URLs in markdown format',\n 'Include key service/product pages and their descriptions',\n 'Deploy and verify access at yourdomain.com/llms.txt',\n ],\n codeExample: `# Site Name\\n> One-line site description\\n\\n## Core Pages\\n- [About](/about): Company overview\\n- [Services](/services): Service offerings\\n- [Blog](/blog): Latest articles\\n\\n## Key Topics\\n- Topic 1\\n- Topic 2`,\n successCriteria: '/llms.txt returns 200 with valid markdown content',\n });\n }\n if (c.score <= 3) {\n fixes.push({\n id: 'fix-llms-txt-full',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add llms-full.txt with extended content',\n description: 'Create a comprehensive llms-full.txt with detailed page descriptions, content summaries, and topic taxonomy.',\n impact: 'medium', effort: 'low',\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create llms-full.txt alongside llms.txt',\n 'Include full page descriptions with word counts',\n 'Add topic categories and content clusters',\n 'Link from llms.txt to llms-full.txt',\n ],\n successCriteria: '/llms-full.txt returns 200 with comprehensive site map',\n });\n }\n return fixes;\n },\n\n schema_markup: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_markup', c.score);\n const affected = getAffectedPages('schema_markup', pages);\n const fixes: FixAction[] = [{\n id: 'fix-schema-markup',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add JSON-LD structured data',\n description: 'Implement Organization, WebSite, and page-specific schema.org JSON-LD to help AI engines extract your content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Organization JSON-LD to your homepage with name, url, logo, sameAs',\n 'Add WebSite schema with SearchAction',\n 'Add page-specific schema (Article, Service, Product, FAQPage) to relevant pages',\n 'Validate with Google Rich Results Test',\n ],\n codeExample: `<script type=\"application/ld+json\">\\n{\\n \"@context\": \"https://schema.org\",\\n \"@type\": \"Organization\",\\n \"name\": \"Your Company\",\\n \"url\": \"https://example.com\",\\n \"logo\": \"https://example.com/logo.png\",\\n \"sameAs\": [\\n \"https://twitter.com/company\",\\n \"https://linkedin.com/company/company\"\\n ]\\n}\\n</script>`,\n successCriteria: 'Homepage and key pages have valid JSON-LD schema',\n dependsOn: ['fix-clean-html-structure'],\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n qa_content_format: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('qa_content_format', c.score);\n const affected = getAffectedPages('qa_content_format', pages);\n return [{\n id: 'fix-qa-format',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add question-based headings',\n description: 'Restructure content with H2/H3 question headings that match how users query AI assistants.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify top user questions for each page topic',\n 'Convert section headings to question format (What, How, Why, When)',\n 'Follow each question heading with a direct 2-3 sentence answer',\n 'Add a summary answer box at the top of long-form content',\n ],\n successCriteria: 'At least 50% of H2/H3 headings use question format',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n clean_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('clean_html', c.score);\n const affected = getAffectedPages('clean_html', pages);\n const fixes: FixAction[] = [{\n id: 'fix-clean-html-structure',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix HTML structure and meta tags',\n description: 'Ensure clean, well-structured HTML with proper meta tags, HTTPS, and parseable content for AI crawlers.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Enable HTTPS and redirect HTTP to HTTPS',\n 'Add proper <title>, meta description, and viewport meta tags',\n 'Fix HTML validation errors (unclosed tags, invalid nesting)',\n 'Ensure content is server-rendered (not client-side only)',\n ],\n successCriteria: 'Pages pass HTML validation with proper meta tags and HTTPS',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n entity_consistency: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_consistency', c.score);\n return [{\n id: 'fix-entity-consistency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Strengthen entity authority (NAP)',\n description: 'Add consistent name, address, phone (NAP) and sameAs links across all pages to strengthen entity recognition.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Ensure company name is consistent across all pages',\n 'Add Organization schema with full NAP details',\n 'Include sameAs links to social profiles and directories',\n 'Add logo and brand marks consistently',\n ],\n successCriteria: 'Organization schema present with consistent NAP on all pages',\n }];\n },\n\n robots_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('robots_txt', c.score);\n return [{\n id: 'fix-robots-txt',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Configure robots.txt for AI crawlers',\n description: 'Update robots.txt to explicitly allow AI crawlers and include sitemap directive.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create or update robots.txt at domain root',\n 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot',\n 'Include Sitemap directive pointing to sitemap.xml',\n 'Verify no accidental Disallow rules blocking content pages',\n ],\n codeExample: `User-agent: *\\nAllow: /\\n\\nUser-agent: GPTBot\\nAllow: /\\n\\nUser-agent: ClaudeBot\\nAllow: /\\n\\nUser-agent: PerplexityBot\\nAllow: /\\n\\nSitemap: https://example.com/sitemap.xml`,\n successCriteria: 'robots.txt returns 200 with AI crawler directives and Sitemap',\n }];\n },\n\n faq_section: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('faq_section', c.score);\n const affected = getAffectedPages('faq_section', pages);\n return [{\n id: 'fix-faq-section',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Build FAQ sections with schema',\n description: 'Create FAQ content with FAQPage schema markup on key pages to become a direct answer source for AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 8-10 most common customer questions per service area',\n 'Create dedicated FAQ page with categorized Q&A pairs',\n 'Add inline FAQ sections to key service/product pages',\n 'Implement FAQPage JSON-LD schema on all FAQ content',\n ],\n successCriteria: 'FAQ page exists with FAQPage schema, key pages have inline FAQ sections',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n original_data: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('original_data', c.score);\n const affected = getAffectedPages('original_data', pages);\n return [{\n id: 'fix-original-data',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add original data and case studies',\n description: 'Publish proprietary data, statistics, case studies, or research that AI engines cannot find elsewhere.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify internal data assets (customer metrics, case study results, survey data)',\n 'Create data-driven content with specific numbers and percentages',\n 'Publish case studies with measurable outcomes',\n 'Add comparison tables with proprietary benchmarks',\n ],\n successCriteria: 'At least 3 pages contain original data points not found elsewhere online',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n internal_linking: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('internal_linking', c.score);\n const fixes: FixAction[] = [];\n\n // Orphan pages fix\n if (linkGraph) {\n const orphans: string[] = [];\n linkGraph.nodes.forEach((node) => {\n if (node.isOrphan) orphans.push(node.url);\n });\n if (orphans.length > 0) {\n fixes.push({\n id: 'fix-internal-linking-orphans',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Link orphan pages into site navigation',\n description: `${orphans.length} pages have no incoming internal links. These are invisible to AI crawlers that follow links.`,\n impact: orphans.length > 5 ? 'critical' : 'high',\n effort: orphans.length > 10 ? 'medium' : 'low',\n impactScore: 0,\n category: 'structure',\n steps: [\n `Identify the ${orphans.length} orphan pages with zero incoming links`,\n 'Add contextual links from related content pages',\n 'Include orphan pages in navigation menus or footer links',\n 'Add \"Related Content\" sections on relevant pages',\n ],\n successCriteria: 'All content pages have at least 1 incoming internal link',\n affectedPages: orphans.slice(0, 20),\n pageCount: orphans.length,\n });\n }\n\n // Depth fix\n if (linkGraph.stats.maxDepth > 3) {\n fixes.push({\n id: 'fix-internal-linking-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce page depth for deep content',\n description: `Max depth is ${linkGraph.stats.maxDepth} clicks from homepage. AI crawlers rarely follow links beyond 3 levels.`,\n impact: 'medium',\n effort: 'medium',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify pages more than 3 clicks from the homepage',\n 'Add direct links from high-level pages to deep content',\n 'Consider flattening URL structure for key pages',\n 'Add hub pages that aggregate related deep content',\n ],\n successCriteria: 'All important content pages reachable within 3 clicks from homepage',\n });\n }\n\n // No clusters fix\n if (linkGraph.clusters.length === 0) {\n fixes.push({\n id: 'fix-internal-linking-clusters',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create topic clusters with pillar pages',\n description: 'No topic clusters detected. Organizing content into pillar-spoke clusters strengthens topical authority for AI engines.',\n impact: 'high',\n effort: 'high',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify 3-5 core topic areas for your business',\n 'Create comprehensive pillar pages (3000+ words) for each topic',\n 'Write 5-7 supporting articles per pillar linking back to pillar',\n 'Interlink supporting articles within each cluster',\n ],\n successCriteria: 'At least 2 topic clusters with pillar page and 5+ spoke pages',\n });\n }\n } else {\n // No link graph available - generic fix\n fixes.push({\n id: 'fix-internal-linking-generic',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve internal linking architecture',\n description: 'Strengthen internal linking with descriptive anchor text between related pages.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Audit current internal link structure',\n 'Add contextual links between related content pages',\n 'Ensure every key page is reachable within 3 clicks from homepage',\n 'Use descriptive anchor text instead of \"click here\" or \"read more\"',\n ],\n successCriteria: 'Key pages have 3+ incoming internal links with descriptive anchors',\n });\n }\n return fixes;\n },\n\n semantic_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('semantic_html', c.score);\n const affected = getAffectedPages('semantic_html', pages);\n return [{\n id: 'fix-semantic-html',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Implement semantic HTML5 elements',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Wrap main content in <main> element',\n 'Use <article> for self-contained content blocks',\n 'Add <nav> for navigation and <aside> for sidebars',\n 'Add lang attribute to <html> and ARIA labels for accessibility',\n ],\n successCriteria: 'Pages use semantic HTML5 elements with lang attribute',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_freshness: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_freshness', c.score);\n const affected = getAffectedPages('content_freshness', pages);\n return [{\n id: 'fix-content-freshness',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add content freshness signals',\n description: 'Include dateModified schema, visible dates, and recent content updates to signal freshness to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add datePublished and dateModified to Article schema',\n 'Display visible \"Last updated\" dates on content pages',\n 'Update stale content with current information',\n 'Add <time> elements with datetime attributes for all dates',\n ],\n successCriteria: 'Content pages show visible dates and have dateModified in schema',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n sitemap_completeness: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('sitemap_completeness', c.score);\n return [{\n id: 'fix-sitemap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create complete sitemap.xml',\n description: 'Generate a comprehensive sitemap with lastmod dates for all important pages.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Generate sitemap.xml listing all content pages',\n 'Include <lastmod> dates for each URL',\n 'Set <changefreq> and <priority> appropriately',\n 'Reference sitemap in robots.txt',\n ],\n successCriteria: 'sitemap.xml returns 200 with all content pages and lastmod dates',\n }];\n },\n\n rss_feed: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('rss_feed', c.score);\n return [{\n id: 'fix-rss-feed',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Deploy RSS/Atom feed',\n description: 'Add an RSS or Atom feed for your blog/news content to signal active publishing to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create RSS 2.0 or Atom feed for blog/news content',\n 'Include title, description, pubDate, and full content for each item',\n 'Add <link rel=\"alternate\" type=\"application/rss+xml\"> to page head',\n 'Auto-generate feed on each new publish',\n ],\n codeExample: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n <channel>\\n <title>Your Site Blog</title>\\n <link>https://example.com/blog</link>\\n <description>Latest articles</description>\\n <item>\\n <title>Article Title</title>\\n <link>https://example.com/blog/article</link>\\n <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>\\n <description>Article summary</description>\\n </item>\\n </channel>\\n</rss>`,\n successCriteria: 'RSS feed returns valid XML with recent content items',\n }];\n },\n\n table_list_extractability: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('table_list_extractability', c.score);\n const affected = getAffectedPages('table_list_extractability', pages);\n return [{\n id: 'fix-tables-lists',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add structured tables and lists',\n description: 'Use HTML tables for comparison data and lists for features, steps, and specifications.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify data suitable for table format (comparisons, pricing, specs)',\n 'Convert bullet points to proper <ul>/<ol> lists',\n 'Add comparison tables with <th> headers',\n 'Ensure tables have descriptive captions',\n ],\n successCriteria: 'Key pages contain at least one HTML table or structured list',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n definition_patterns: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('definition_patterns', c.score);\n const affected = getAffectedPages('definition_patterns', pages);\n return [{\n id: 'fix-definitions',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add definition-style content',\n description: 'Include clear definition patterns for key terms and concepts that AI engines can cite for \"what is\" queries.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify key industry terms your audience searches for',\n 'Write clear definitions using \"X is...\" or \"X refers to...\" patterns',\n 'Place definitions near the top of relevant pages',\n 'Consider a glossary page for comprehensive term coverage',\n ],\n successCriteria: 'Key pages contain definition patterns for relevant terms',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n direct_answer_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('direct_answer_density', c.score);\n const affected = getAffectedPages('direct_answer_density', pages);\n return [{\n id: 'fix-direct-answers',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add direct answer paragraphs',\n description: 'Write concise, standalone answer paragraphs after question headings for AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify question-format headings on each page',\n 'Write a 2-3 sentence direct answer immediately after each heading',\n 'Ensure answers are self-contained (don\\'t require context from other sections)',\n 'Use bold for key facts within answer paragraphs',\n ],\n successCriteria: 'Question headings are followed by direct, concise answer paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_licensing: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_licensing', c.score);\n return [{\n id: 'fix-content-licensing',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add ai.txt and content licensing',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to structured data.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create ai.txt at domain root with usage permissions',\n 'Specify allowed AI uses (training, citation, summarization)',\n 'Add license information to schema markup',\n 'Consider a content licensing page linked from footer',\n ],\n codeExample: `# ai.txt - AI Usage Policy for example.com\\n\\nUser-Agent: *\\nAllow: /blog/\\nAllow: /docs/\\n\\n# Permissions\\nTraining: yes\\nCitation: yes with attribution\\nSummarization: yes`,\n successCriteria: '/ai.txt returns 200 with clear AI usage permissions',\n }];\n },\n\n author_schema_depth: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('author_schema_depth', c.score);\n return [{\n id: 'fix-author-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Enhance author and expert schema',\n description: 'Add Person schema for content authors with credentials and sameAs links for E-E-A-T signals.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create author profile pages for content creators',\n 'Add Person schema with name, jobTitle, credentials, sameAs',\n 'Link articles to author profiles via schema author property',\n 'Include author bio and expertise on article pages',\n ],\n successCriteria: 'Articles have Person schema for authors with credentials',\n }];\n },\n\n fact_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('fact_density', c.score);\n const affected = getAffectedPages('fact_density', pages);\n return [{\n id: 'fix-fact-density',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase fact and data density',\n description: 'Add specific numbers, percentages, statistics, and data points that AI engines can cite.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Review content for vague claims and replace with specific data',\n 'Add statistics, percentages, and measurable outcomes',\n 'Include source citations for data points',\n 'Add data tables or comparison charts where appropriate',\n ],\n successCriteria: 'Key pages contain at least 3 specific data points per 500 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n canonical_url: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('canonical_url', c.score);\n const affected = getAffectedPages('canonical_url', pages);\n return [{\n id: 'fix-canonical-url',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix canonical URL strategy',\n description: 'Add rel=\"canonical\" tags to all pages to prevent duplicate content confusion.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Add <link rel=\"canonical\"> to every page pointing to preferred URL',\n 'Ensure canonical URLs use consistent scheme (https) and format',\n 'Handle www vs non-www with proper redirects',\n 'Set canonical for paginated content to the main page',\n ],\n codeExample: `<link rel=\"canonical\" href=\"https://example.com/page\" />`,\n successCriteria: 'All pages have rel=\"canonical\" pointing to the correct URL',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_velocity: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_velocity', c.score);\n return [{\n id: 'fix-content-velocity',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase publishing frequency',\n description: 'Establish a regular content publishing cadence to signal active, current information to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Set a publishing schedule (weekly or bi-weekly minimum)',\n 'Create a content calendar covering key topics',\n 'Update sitemap and RSS feed with each new publish',\n 'Refresh existing evergreen content with current data',\n ],\n successCriteria: 'At least 2 new or updated content pages per month with dated entries',\n }];\n },\n\n schema_coverage: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_coverage', c.score);\n return [{\n id: 'fix-schema-coverage',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Extend schema to inner pages',\n description: 'Add page-specific structured data beyond the homepage to articles, services, and product pages.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Article schema to blog/news pages',\n 'Add Service/Product schema to service/product pages',\n 'Add BreadcrumbList schema to all inner pages',\n 'Validate each page type with Rich Results Test',\n ],\n successCriteria: 'At least 50% of content pages have page-specific schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n speakable_schema: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('speakable_schema', c.score);\n return [{\n id: 'fix-speakable-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add SpeakableSpecification schema',\n description: 'Add Speakable schema to tell voice assistants which content sections are best for spoken answers.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Identify key paragraphs suitable for voice readout',\n 'Add SpeakableSpecification with CSS selectors to Article schema',\n 'Point speakable selectors to headline and summary paragraphs',\n 'Test with Google structured data testing tool',\n ],\n codeExample: `\"speakable\": {\\n \"@type\": \"SpeakableSpecification\",\\n \"cssSelector\": [\\n \".article-headline\",\\n \".article-summary\"\\n ]\\n}`,\n successCriteria: 'Article pages include SpeakableSpecification in schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n query_answer_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('query_answer_alignment', c.score);\n const affected = getAffectedPages('query_answer_alignment', pages);\n return [{\n id: 'fix-query-answer-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve query-answer alignment',\n description: 'Ensure question headings are followed by direct, concise answers in the first paragraph.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit question-format headings and their following paragraphs',\n 'Add direct answers in the first 1-2 sentences after each question heading',\n 'Remove filler text between question and answer',\n 'Ensure answers are self-contained and citable',\n ],\n successCriteria: 'Question headings have direct answer paragraphs within 50 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_cannibalization: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_cannibalization', c.score);\n const fixes: FixAction[] = [];\n\n if (linkGraph && linkGraph.clusters.length > 0) {\n // Check for low-cohesion clusters which may indicate overlapping content\n const lowCohesion = linkGraph.clusters.filter(cl => cl.cohesion < 50);\n if (lowCohesion.length > 0) {\n const affected = lowCohesion.flatMap(cl => [cl.pillarUrl, ...cl.spokes]);\n fixes.push({\n id: 'fix-content-cannibalization-overlap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Consolidate overlapping content',\n description: `${lowCohesion.length} content clusters have low cohesion, suggesting pages compete for the same topics.`,\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify pages targeting the same keywords or topics',\n 'Merge overlapping pages into single authoritative pages',\n 'Set up 301 redirects from merged pages to consolidated page',\n 'Differentiate remaining similar pages with distinct angles',\n ],\n successCriteria: 'No two pages target the same primary keyword or topic',\n affectedPages: affected.slice(0, 20),\n pageCount: affected.length,\n });\n }\n }\n\n if (fixes.length === 0) {\n const affected = getAffectedPages('content_cannibalization', pages);\n fixes.push({\n id: 'fix-content-cannibalization',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Resolve content cannibalization',\n description: 'Multiple pages may be targeting the same topics, diluting AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit pages for overlapping topic coverage',\n 'Consolidate similar pages into comprehensive single pages',\n 'Differentiate remaining pages with distinct angles and keywords',\n 'Add canonical tags to prevent duplicate content issues',\n ],\n successCriteria: 'Each topic is covered by a single authoritative page',\n affectedPages: affected,\n pageCount: affected?.length,\n });\n }\n\n return fixes;\n },\n\n visible_date_signal: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('visible_date_signal', c.score);\n const affected = getAffectedPages('visible_date_signal', pages);\n return [{\n id: 'fix-visible-dates',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add visible date signals',\n description: 'Add visible publication and modification dates using <time> elements for AI engine freshness assessment.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add visible \"Published\" and \"Last updated\" dates to content pages',\n 'Use <time> elements with datetime attributes',\n 'Ensure dates match dateModified in schema markup',\n 'Update dates when content is refreshed',\n ],\n codeExample: `<time datetime=\"2024-01-15\">January 15, 2024</time>`,\n successCriteria: 'Content pages show visible dates with <time> elements',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n topic_coherence: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('topic_coherence', c.score);\n return [{\n id: 'fix-topic-coherence',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Focus blog content on core expertise',\n description: 'Ensure blog content consistently covers your core topic areas. Scattered content across unrelated topics weakens AI engine authority signals.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 2-3 core expertise areas your brand is known for',\n 'Audit existing blog posts and remove or consolidate off-topic content',\n 'Create a content calendar focused on core topics',\n 'Use topic clusters: pillar pages linking to supporting articles within the same niche',\n ],\n successCriteria: '80%+ of blog content covers core expertise areas with consistent topic focus',\n }];\n },\n\n content_depth: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_depth', c.score);\n const affected = getAffectedPages('content_depth', pages);\n return [{\n id: 'fix-content-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase content depth and structure',\n description: 'Expand thin content with more detail, examples, and structured sections. AI engines prefer comprehensive articles with clear heading hierarchies.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Aim for 1000+ words per article with expert analysis and examples',\n 'Use H2/H3 subheadings every 200-300 words for clear structure',\n 'Add comparison tables, numbered steps, and data points',\n 'Remove or expand thin pages (under 300 words) that dilute site quality',\n ],\n successCriteria: 'Average article length exceeds 1000 words with 5+ subheadings per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n};\n\n// ─── Main function ──────────────────────────────────────────────────────────\n\nexport function generateFixPlan(\n domain: string,\n overallScore: number,\n criteria: CriterionResult[],\n pagesReviewed?: PageReview[],\n linkGraph?: LinkGraph,\n): FixPlan {\n // Step 1: Generate all fixes from criterion scores\n const allFixes: FixAction[] = [];\n\n for (const criterion of criteria) {\n const generator = FIX_GENERATORS[criterion.criterion];\n if (!generator) continue;\n\n const fixes = generator(criterion, pagesReviewed, linkGraph);\n for (const fix of fixes) {\n const weight = CRITERION_WEIGHTS[criterion.criterion] ?? 0.05;\n fix.impactScore = Math.round((10 - criterion.score) * weight * 100);\n allFixes.push(fix);\n }\n }\n\n // Step 2: Group into phases\n const phases: FixPhase[] = PHASE_CONFIG.map(config => {\n const phaseFixes = allFixes\n .filter(fix => config.criteria.includes(fix.criterionId))\n .sort((a, b) => b.impactScore - a.impactScore);\n\n return {\n phase: config.phase,\n title: config.title,\n description: config.description,\n fixes: phaseFixes,\n estimatedImpact: 0, // calculated after projected score\n };\n });\n\n // Step 3: Validate dependencies (move fixes if dependency is in later phase)\n for (const phase of phases) {\n for (const fix of phase.fixes) {\n if (!fix.dependsOn) continue;\n for (const depId of fix.dependsOn) {\n // Find which phase the dependency is in\n const depPhase = phases.find(p => p.fixes.some(f => f.id === depId));\n if (depPhase && depPhase.phase > phase.phase) {\n // Dependency is in a later phase - move this fix there\n phase.fixes = phase.fixes.filter(f => f.id !== fix.id);\n depPhase.fixes.push(fix);\n depPhase.fixes.sort((a, b) => b.impactScore - a.impactScore);\n break;\n }\n }\n }\n }\n\n // Step 4: Calculate projected score\n const totalWeight = Object.values(CRITERION_WEIGHTS).reduce((s, w) => s + w, 0);\n\n // Deduplicate: only count the best fix per criterion\n const bestDeltaPerCriterion = new Map<string, number>();\n for (const fix of allFixes) {\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n const delta = (improvement * weight / totalWeight) * 100;\n const existing = bestDeltaPerCriterion.get(fix.criterionId) ?? 0;\n if (delta > existing) bestDeltaPerCriterion.set(fix.criterionId, delta);\n }\n const scoreDelta = Array.from(bestDeltaPerCriterion.values()).reduce((s, d) => s + d, 0);\n\n const projectedScore = Math.min(100, Math.round(overallScore + scoreDelta));\n\n // Step 5: Calculate per-phase estimated impact\n for (const phase of phases) {\n let phaseImpact = 0;\n const seenCriteria = new Set<string>();\n for (const fix of phase.fixes) {\n if (seenCriteria.has(fix.criterionId)) continue;\n seenCriteria.add(fix.criterionId);\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n phaseImpact += (improvement * weight / totalWeight) * 100;\n }\n phase.estimatedImpact = Math.round(phaseImpact);\n }\n\n // Step 6: Extract quick wins\n const quickWins = allFixes.filter(\n f => (f.effort === 'trivial' || f.effort === 'low') && (f.impact === 'critical' || f.impact === 'high'),\n );\n\n // Step 7: Build summary\n const summary: FixPlanSummary = {\n criticalCount: allFixes.filter(f => f.impact === 'critical').length,\n highCount: allFixes.filter(f => f.impact === 'high').length,\n mediumCount: allFixes.filter(f => f.impact === 'medium').length,\n lowCount: allFixes.filter(f => f.impact === 'low').length,\n quickWinCount: quickWins.length,\n topOpportunity: allFixes.length > 0\n ? allFixes.sort((a, b) => b.impactScore - a.impactScore)[0].title\n : 'None',\n estimatedTotalEffort: formatEffort(allFixes.reduce((s, f) => s + effortToHours(f.effort), 0)),\n };\n\n return {\n domain,\n generatedAt: new Date().toISOString(),\n overallScore,\n projectedScore,\n totalFixes: allFixes.length,\n phases,\n quickWins,\n summary,\n };\n}\n\nfunction formatEffort(hours: number): string {\n if (hours < 1) return '<1h';\n return `~${Math.round(hours)}h`;\n}\n","/**\n * HTML report generator for AEORank audits.\n * Produces self-contained HTML with inline CSS - zero external dependencies.\n */\n\nimport type { AuditResult } from './audit.js';\n\n// ─── Score colors (matching monorepo lib/score-color.ts) ─────────────────────\n\nfunction scoreColor(score: number): string {\n if (score <= 40) return '#F44336';\n if (score <= 55) return '#FF9800';\n if (score <= 70) return '#FFC107';\n if (score <= 85) return '#4CAF50';\n return '#2E7D32';\n}\n\nfunction criterionColor(score: number): string {\n return scoreColor(score * 10);\n}\n\n// ─── Security: escape HTML entities ──────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n// ─── SVG score circle ────────────────────────────────────────────────────────\n\nfunction scoreCircleSvg(score: number, size = 160): string {\n const radius = (size - 16) / 2;\n const circumference = 2 * Math.PI * radius;\n const progress = (score / 100) * circumference;\n const color = scoreColor(score);\n\n return `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\">\n <circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${radius}\" fill=\"none\" stroke=\"#e0e0e0\" stroke-width=\"10\"/>\n <circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${radius}\" fill=\"none\" stroke=\"${color}\" stroke-width=\"10\"\n stroke-dasharray=\"${circumference}\" stroke-dashoffset=\"${circumference - progress}\"\n stroke-linecap=\"round\" transform=\"rotate(-90 ${size / 2} ${size / 2})\"/>\n <text x=\"${size / 2}\" y=\"${size / 2 + 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n font-size=\"42\" font-weight=\"700\" fill=\"${color}\">${score}</text>\n <text x=\"${size / 2}\" y=\"${size / 2 + 24}\" text-anchor=\"middle\" dominant-baseline=\"middle\"\n font-size=\"13\" fill=\"#666\">/100</text>\n </svg>`;\n}\n\n// ─── Inline CSS ──────────────────────────────────────────────────────────────\n\nconst CSS = `\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1a1a1a; background: #f8f9fa; line-height: 1.6; }\n .container { max-width: 1100px; margin: 0 auto; padding: 32px 24px; }\n .header { text-align: center; margin-bottom: 40px; }\n .header h1 { font-size: 28px; font-weight: 700; margin-bottom: 4px; }\n .header .date { color: #666; font-size: 14px; }\n .score-section { display: flex; justify-content: center; margin: 24px 0 32px; }\n .verdict { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px 24px; margin-bottom: 32px; font-size: 15px; color: #333; }\n .section-title { font-size: 20px; font-weight: 700; margin-bottom: 16px; color: #1a1a1a; }\n .scorecard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .criterion-card { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 14px 16px; display: flex; align-items: center; gap: 12px; }\n .criterion-bar { width: 80px; height: 8px; background: #e0e0e0; border-radius: 4px; flex-shrink: 0; overflow: hidden; }\n .criterion-bar-fill { height: 100%; border-radius: 4px; }\n .criterion-score { font-size: 14px; font-weight: 700; min-width: 32px; text-align: center; }\n .criterion-name { font-size: 13px; flex: 1; }\n .criterion-status { font-size: 11px; color: #666; background: #f0f0f0; padding: 2px 8px; border-radius: 10px; white-space: nowrap; }\n table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; margin-bottom: 32px; }\n th { background: #f5f5f5; text-align: left; padding: 10px 14px; font-size: 13px; font-weight: 600; color: #555; border-bottom: 1px solid #e0e0e0; }\n td { padding: 10px 14px; font-size: 13px; border-bottom: 1px solid #f0f0f0; }\n .impact-badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; color: #fff; }\n .impact-critical { background: #F44336; }\n .impact-high { background: #FF5722; }\n .impact-quick-win { background: #4CAF50; }\n .impact-core-aeo { background: #2196F3; }\n .impact-medium { background: #FF9800; }\n .impact-low { background: #9E9E9E; }\n .impact-big-opportunity { background: #9C27B0; }\n .impact-measurement { background: #607D8B; }\n .bottom-line { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px 24px; margin-bottom: 32px; font-size: 15px; }\n .footer { text-align: center; color: #999; font-size: 12px; padding: 24px 0; border-top: 1px solid #e0e0e0; margin-top: 24px; }\n .compare-header { display: flex; justify-content: center; align-items: center; gap: 48px; margin-bottom: 32px; }\n .compare-site { text-align: center; }\n .compare-site h2 { font-size: 20px; margin-bottom: 8px; }\n .compare-vs { font-size: 24px; font-weight: 700; color: #999; }\n .delta-positive { color: #4CAF50; font-weight: 600; }\n .delta-negative { color: #F44336; font-weight: 600; }\n .delta-zero { color: #999; }\n .summary-box { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px 20px; margin-bottom: 24px; display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; }\n .summary-stat { text-align: center; }\n .summary-stat .num { font-size: 28px; font-weight: 700; }\n .summary-stat .label { font-size: 12px; color: #666; }\n @media print {\n body { background: #fff; }\n .container { padding: 0; }\n .criterion-card, .verdict, .bottom-line, table { break-inside: avoid; }\n }\n @media (max-width: 640px) {\n .scorecard-grid { grid-template-columns: 1fr; }\n .compare-header { flex-direction: column; gap: 16px; }\n }\n`;\n\n// ─── Impact badge class ──────────────────────────────────────────────────────\n\nfunction impactClass(impact: string): string {\n const key = impact.toLowerCase().replace(/\\s+/g, '-');\n return `impact-${key}`;\n}\n\n// ─── Single audit report ─────────────────────────────────────────────────────\n\nexport function generateHtmlReport(result: AuditResult): string {\n const domain = escapeHtml(result.site);\n const date = escapeHtml(result.auditDate);\n\n const scorecardCards = result.scorecard\n .map((item) => {\n const color = criterionColor(item.score);\n const width = item.score * 10;\n return `<div class=\"criterion-card\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${width}%;background:${color}\"></div></div>\n <span class=\"criterion-score\" style=\"color:${color}\">${item.score}/10</span>\n <span class=\"criterion-name\">${escapeHtml(item.criterion)}</span>\n <span class=\"criterion-status\">${escapeHtml(item.status)}</span>\n </div>`;\n })\n .join('\\n');\n\n const opportunityRows = result.opportunities\n .map((opp) => {\n const cls = impactClass(opp.impact);\n return `<tr>\n <td>${opp.id}</td>\n <td>${escapeHtml(opp.name)}</td>\n <td><span class=\"impact-badge ${cls}\">${escapeHtml(opp.impact)}</span></td>\n <td>${escapeHtml(opp.effort)}</td>\n <td>${escapeHtml(opp.description)}</td>\n </tr>`;\n })\n .join('\\n');\n\n const pagesReviewed = result.pagesReviewed || [];\n const pagesRows = pagesReviewed\n .map((page) => {\n const issueCount = page.issues.length;\n const strengthCount = page.strengths.length;\n const aeoDisplay = page.aeoScore != null ? `<span style=\"font-weight:600;color:${scoreColor(page.aeoScore)}\">${page.aeoScore}</span>` : '-';\n return `<tr>\n <td>${escapeHtml(page.url)}</td>\n <td>${escapeHtml(page.category)}</td>\n <td>${page.wordCount}</td>\n <td>${aeoDisplay}</td>\n <td>${issueCount}</td>\n <td>${strengthCount}</td>\n </tr>`;\n })\n .join('\\n');\n\n const scoredPages = pagesReviewed.filter(p => p.aeoScore != null);\n const avgPageScore = scoredPages.length > 0\n ? Math.round(scoredPages.reduce((sum, p) => sum + p.aeoScore!, 0) / scoredPages.length)\n : null;\n\n const now = new Date().toISOString();\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>AEORank Report - ${domain}</title>\n <style>${CSS}</style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>${domain}</h1>\n <div class=\"date\">AEO Audit - ${date}</div>\n </div>\n\n <div class=\"score-section\">\n ${scoreCircleSvg(result.overallScore)}\n </div>\n\n <div class=\"verdict\">${escapeHtml(result.verdict)}</div>\n\n <h2 class=\"section-title\">Scorecard (26 Criteria)</h2>\n <div class=\"scorecard-grid\">\n ${scorecardCards}\n </div>\n\n ${result.opportunities.length > 0 ? `\n <h2 class=\"section-title\">Opportunities (${result.opportunities.length})</h2>\n <table>\n <thead><tr><th>#</th><th>Opportunity</th><th>Impact</th><th>Effort</th><th>Description</th></tr></thead>\n <tbody>${opportunityRows}</tbody>\n </table>\n ` : ''}\n\n ${pagesReviewed.length > 0 ? `\n <h2 class=\"section-title\">Pages Reviewed (${pagesReviewed.length})</h2>\n ${avgPageScore != null ? `<div class=\"summary-box\"><div class=\"summary-stat\"><div class=\"num\" style=\"color:${scoreColor(avgPageScore)}\">${avgPageScore}</div><div class=\"label\">Avg Page AEO Score</div></div></div>` : ''}\n <table>\n <thead><tr><th>URL</th><th>Category</th><th>Words</th><th>AEO Score</th><th>Issues</th><th>Strengths</th></tr></thead>\n <tbody>${pagesRows}</tbody>\n </table>\n ` : ''}\n\n <div class=\"bottom-line\"><strong>Bottom line:</strong> ${escapeHtml(result.bottomLine)}</div>\n\n <div class=\"footer\">Generated by AEORank - ${now}</div>\n </div>\n</body>\n</html>`;\n}\n\n// ─── Comparison report types (imported from compare.ts at runtime) ───────────\n\ninterface CriterionComparisonLike {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\ninterface ComparisonResultLike {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparisonLike[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n// ─── Comparison HTML report ──────────────────────────────────────────────────\n\nexport function generateComparisonHtmlReport(result: ComparisonResultLike): string {\n const domainA = escapeHtml(result.siteA.site);\n const domainB = escapeHtml(result.siteB.site);\n const scoreA = result.siteA.overallScore;\n const scoreB = result.siteB.overallScore;\n\n const criteriaRows = result.comparison.criteria\n .map((c) => {\n const colorA = criterionColor(c.scoreA);\n const colorB = criterionColor(c.scoreB);\n const widthA = c.scoreA * 10;\n const widthB = c.scoreB * 10;\n let deltaHtml: string;\n if (c.delta > 0) deltaHtml = `<span class=\"delta-positive\">+${c.delta}</span>`;\n else if (c.delta < 0) deltaHtml = `<span class=\"delta-negative\">${c.delta}</span>`;\n else deltaHtml = `<span class=\"delta-zero\">0</span>`;\n\n return `<tr>\n <td>${c.id}</td>\n <td>${escapeHtml(c.criterion)}</td>\n <td>\n <div style=\"display:flex;align-items:center;gap:8px\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${widthA}%;background:${colorA}\"></div></div>\n <span style=\"color:${colorA};font-weight:600\">${c.scoreA}</span>\n </div>\n </td>\n <td>\n <div style=\"display:flex;align-items:center;gap:8px\">\n <div class=\"criterion-bar\"><div class=\"criterion-bar-fill\" style=\"width:${widthB}%;background:${colorB}\"></div></div>\n <span style=\"color:${colorB};font-weight:600\">${c.scoreB}</span>\n </div>\n </td>\n <td style=\"text-align:center\">${deltaHtml}</td>\n </tr>`;\n })\n .join('\\n');\n\n const advantagesA = result.comparison.siteAAdvantages.length;\n const advantagesB = result.comparison.siteBAdvantages.length;\n const tied = result.comparison.tied.length;\n\n const now = new Date().toISOString();\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>AEORank Comparison - ${domainA} vs ${domainB}</title>\n <style>${CSS}</style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>AEO Comparison</h1>\n </div>\n\n <div class=\"compare-header\">\n <div class=\"compare-site\">\n <h2>${domainA}</h2>\n ${scoreCircleSvg(scoreA, 120)}\n </div>\n <div class=\"compare-vs\">vs</div>\n <div class=\"compare-site\">\n <h2>${domainB}</h2>\n ${scoreCircleSvg(scoreB, 120)}\n </div>\n </div>\n\n <div class=\"summary-box\">\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:${scoreColor(scoreA)}\">${advantagesA}</div>\n <div class=\"label\">${domainA} leads</div>\n </div>\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:${scoreColor(scoreB)}\">${advantagesB}</div>\n <div class=\"label\">${domainB} leads</div>\n </div>\n <div class=\"summary-stat\">\n <div class=\"num\" style=\"color:#999\">${tied}</div>\n <div class=\"label\">Tied</div>\n </div>\n </div>\n\n <h2 class=\"section-title\">Per-Criterion Comparison</h2>\n <table>\n <thead>\n <tr>\n <th>#</th>\n <th>Criterion</th>\n <th>${domainA}</th>\n <th>${domainB}</th>\n <th>Delta</th>\n </tr>\n </thead>\n <tbody>${criteriaRows}</tbody>\n </table>\n\n ${result.comparison.siteAAdvantages.length > 0 ? `\n <h2 class=\"section-title\">${domainA} Advantages</h2>\n <div class=\"verdict\">${result.comparison.siteAAdvantages.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n ${result.comparison.siteBAdvantages.length > 0 ? `\n <h2 class=\"section-title\">${domainB} Advantages</h2>\n <div class=\"verdict\">${result.comparison.siteBAdvantages.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n ${result.comparison.tied.length > 0 ? `\n <h2 class=\"section-title\">Tied Criteria</h2>\n <div class=\"verdict\">${result.comparison.tied.map(c => escapeHtml(c)).join(', ')}</div>\n ` : ''}\n\n <div class=\"footer\">Generated by AEORank - ${now}</div>\n </div>\n</body>\n</html>`;\n}\n","/**\n * Comparison mode - run two audits in parallel and compute per-criterion deltas.\n */\n\nimport { audit } from './audit.js';\nimport type { AuditOptions, AuditResult } from './audit.js';\n\nexport interface CriterionComparison {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\nexport interface ComparisonResult {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparison[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n/**\n * Audit two domains in parallel and build a per-criterion comparison.\n */\nexport async function compare(\n domainA: string,\n domainB: string,\n options?: AuditOptions,\n): Promise<ComparisonResult> {\n const [siteA, siteB] = await Promise.all([\n audit(domainA, options),\n audit(domainB, options),\n ]);\n\n const criteria: CriterionComparison[] = [];\n const siteAAdvantages: string[] = [];\n const siteBAdvantages: string[] = [];\n const tied: string[] = [];\n\n // Zip scorecards by id (both should have 26 items in the same order)\n for (let i = 0; i < siteA.scorecard.length; i++) {\n const a = siteA.scorecard[i];\n const b = siteB.scorecard[i];\n if (!a || !b) continue;\n\n const delta = a.score - b.score;\n\n criteria.push({\n id: a.id,\n criterion: a.criterion,\n scoreA: a.score,\n scoreB: b.score,\n delta,\n statusA: a.status,\n statusB: b.status,\n });\n\n if (delta > 0) siteAAdvantages.push(a.criterion);\n else if (delta < 0) siteBAdvantages.push(a.criterion);\n else tied.push(a.criterion);\n }\n\n return {\n siteA,\n siteB,\n comparison: {\n scoreDelta: siteA.overallScore - siteB.overallScore,\n criteria,\n siteAAdvantages,\n siteBAdvantages,\n tied,\n },\n };\n}\n"],"mappings":"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","names":["text","cap","extractQuestionHeadings","getTextContent","countWords","crawlFullSite","extractTitle","getTextContent","countWords","CRITERION_WEIGHTS","CRITERION_WEIGHTS"]}