aeorank 2.0.0 → 2.1.0

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.d.cts CHANGED
@@ -362,7 +362,7 @@ declare function analyzeAllPages(siteData: SiteData): PageReview[];
362
362
 
363
363
  /**
364
364
  * Per-page AEO scoring.
365
- * Evaluates 14 of 26 criteria that apply at individual page level.
365
+ * Evaluates 14 of 28 criteria that apply at individual page level.
366
366
  * Produces a 0-100 AEO score per page.
367
367
  */
368
368
 
package/dist/index.d.ts CHANGED
@@ -362,7 +362,7 @@ declare function analyzeAllPages(siteData: SiteData): PageReview[];
362
362
 
363
363
  /**
364
364
  * Per-page AEO scoring.
365
- * Evaluates 14 of 26 criteria that apply at individual page level.
365
+ * Evaluates 14 of 28 criteria that apply at individual page level.
366
366
  * Produces a 0-100 AEO score per page.
367
367
  */
368
368
 
package/dist/index.js CHANGED
@@ -2069,40 +2069,58 @@ function auditSiteFromData(data) {
2069
2069
 
2070
2070
  // src/scoring.ts
2071
2071
  var WEIGHTS = {
2072
- // ─── Core Content (high weight - these determine real AI citation quality) ──
2073
- qa_content_format: 0.12,
2074
- original_data: 0.12,
2072
+ // ─── Content Substance (~55%) ─────────────────────────────────────────────
2073
+ // WHY an AI engine would cite you. These drive citation quality directly.
2075
2074
  topic_coherence: 0.14,
2076
- // NEW v2.0: biggest predictor of AI citation quality
2077
- fact_density: 0.08,
2078
- direct_answer_density: 0.07,
2079
- content_depth: 0.06,
2080
- // NEW v2.0: substantive content vs thin pages
2081
- // ─── Structure & Discovery (medium weight - technical readiness) ────────────
2082
- schema_markup: 0.08,
2083
- llms_txt: 0.08,
2084
- clean_html: 0.08,
2085
- entity_consistency: 0.08,
2086
- faq_section: 0.08,
2087
- internal_linking: 0.08,
2088
- // ─── Content Signals (moderate weight) ──────────────────────────────────────
2089
- content_freshness: 0.06,
2090
- table_list_extractability: 0.05,
2091
- query_answer_alignment: 0.06,
2092
- definition_patterns: 0.04,
2093
- author_schema_depth: 0.04,
2094
- content_cannibalization: 0.05,
2095
- visible_date_signal: 0.04,
2096
- semantic_html: 0.04,
2097
- // ─── Plumbing (low weight - nice to have but not what drives citations) ─────
2098
- robots_txt: 0.03,
2099
- sitemap_completeness: 0.03,
2100
- content_velocity: 0.03,
2101
- rss_feed: 0.02,
2102
- content_licensing: 0.03,
2103
- canonical_url: 0.02,
2104
- schema_coverage: 0.02,
2105
- speakable_schema: 0.02
2075
+ // Topical authority - THE gating signal
2076
+ original_data: 0.1,
2077
+ // Unique value AI can't find elsewhere
2078
+ content_depth: 0.07,
2079
+ // Comprehensive vs thin coverage
2080
+ fact_density: 0.06,
2081
+ // Information density per page
2082
+ direct_answer_density: 0.05,
2083
+ // Direct answers to queries
2084
+ qa_content_format: 0.05,
2085
+ // Answer-shaped content structure
2086
+ query_answer_alignment: 0.05,
2087
+ // Relevance to actual AI queries
2088
+ faq_section: 0.04,
2089
+ // Structured Q&A pairs
2090
+ // ─── Content Organization (~30%) ──────────────────────────────────────────
2091
+ // HOW easily AI engines can extract and trust your content.
2092
+ entity_consistency: 0.05,
2093
+ // Brand authority and E-E-A-T
2094
+ internal_linking: 0.04,
2095
+ // Site structure and topic clusters
2096
+ content_freshness: 0.04,
2097
+ // Recency signals
2098
+ schema_markup: 0.03,
2099
+ // Structured data for discovery
2100
+ author_schema_depth: 0.03,
2101
+ // Expert attribution
2102
+ table_list_extractability: 0.03,
2103
+ // Extractable structured data
2104
+ definition_patterns: 0.02,
2105
+ // Clear definitions
2106
+ visible_date_signal: 0.02,
2107
+ // Publication date trust
2108
+ semantic_html: 0.02,
2109
+ // Clean semantic structure
2110
+ clean_html: 0.02,
2111
+ // Parseable markup
2112
+ // ─── Technical Plumbing (~15%) ────────────────────────────────────────────
2113
+ // WHETHER AI crawlers can find you. Table stakes with diminishing returns.
2114
+ content_cannibalization: 0.02,
2115
+ llms_txt: 0.02,
2116
+ robots_txt: 0.02,
2117
+ content_velocity: 0.02,
2118
+ content_licensing: 0.02,
2119
+ sitemap_completeness: 0.01,
2120
+ canonical_url: 0.01,
2121
+ rss_feed: 0.01,
2122
+ schema_coverage: 0.01,
2123
+ speakable_schema: 0.01
2106
2124
  };
2107
2125
  function calculateOverallScore(criteria) {
2108
2126
  let totalWeight = 0;
@@ -2113,7 +2131,13 @@ function calculateOverallScore(criteria) {
2113
2131
  totalWeight += weight;
2114
2132
  }
2115
2133
  if (totalWeight === 0) return 0;
2116
- return Math.round(weightedSum / totalWeight);
2134
+ let score = Math.round(weightedSum / totalWeight);
2135
+ const coherence = criteria.find((c) => c.criterion === "topic_coherence");
2136
+ if (coherence && coherence.score < 6) {
2137
+ const cap2 = 35 + coherence.score * 5;
2138
+ score = Math.min(score, cap2);
2139
+ }
2140
+ return score;
2117
2141
  }
2118
2142
 
2119
2143
  // src/headless-fetch.ts
@@ -2332,32 +2356,37 @@ function buildDetailedFindings(results) {
2332
2356
 
2333
2357
  // src/narrative-generator.ts
2334
2358
  var CRITERION_WEIGHTS = {
2335
- llms_txt: 0.1,
2336
- schema_markup: 0.15,
2337
- qa_content_format: 0.15,
2338
- clean_html: 0.1,
2339
- entity_consistency: 0.1,
2340
- robots_txt: 0.05,
2341
- faq_section: 0.1,
2359
+ // Content Substance (~55%)
2360
+ topic_coherence: 0.14,
2342
2361
  original_data: 0.1,
2343
- internal_linking: 0.1,
2344
- semantic_html: 0.05,
2345
- content_freshness: 0.07,
2346
- sitemap_completeness: 0.05,
2347
- rss_feed: 0.03,
2348
- table_list_extractability: 0.07,
2349
- definition_patterns: 0.04,
2350
- direct_answer_density: 0.07,
2351
- content_licensing: 0.04,
2352
- author_schema_depth: 0.04,
2353
- fact_density: 0.05,
2354
- canonical_url: 0.04,
2355
- content_velocity: 0.03,
2356
- schema_coverage: 0.03,
2357
- speakable_schema: 0.03,
2358
- query_answer_alignment: 0.08,
2359
- content_cannibalization: 0.05,
2360
- visible_date_signal: 0.04
2362
+ content_depth: 0.07,
2363
+ fact_density: 0.06,
2364
+ direct_answer_density: 0.05,
2365
+ qa_content_format: 0.05,
2366
+ query_answer_alignment: 0.05,
2367
+ faq_section: 0.04,
2368
+ // Content Organization (~30%)
2369
+ entity_consistency: 0.05,
2370
+ internal_linking: 0.04,
2371
+ content_freshness: 0.04,
2372
+ schema_markup: 0.03,
2373
+ author_schema_depth: 0.03,
2374
+ table_list_extractability: 0.03,
2375
+ definition_patterns: 0.02,
2376
+ visible_date_signal: 0.02,
2377
+ semantic_html: 0.02,
2378
+ clean_html: 0.02,
2379
+ // Technical Plumbing (~15%)
2380
+ content_cannibalization: 0.02,
2381
+ llms_txt: 0.02,
2382
+ robots_txt: 0.02,
2383
+ content_velocity: 0.02,
2384
+ content_licensing: 0.02,
2385
+ sitemap_completeness: 0.01,
2386
+ canonical_url: 0.01,
2387
+ rss_feed: 0.01,
2388
+ schema_coverage: 0.01,
2389
+ speakable_schema: 0.01
2361
2390
  };
2362
2391
  var OPPORTUNITY_TEMPLATES = {
2363
2392
  llms_txt: {
@@ -2489,6 +2518,16 @@ var OPPORTUNITY_TEMPLATES = {
2489
2518
  name: "Add Visible Date Signals",
2490
2519
  effort: "Low",
2491
2520
  description: "Display publication/modification dates visibly using <time> elements and add datePublished/dateModified to JSON-LD schema."
2521
+ },
2522
+ topic_coherence: {
2523
+ name: "Focus Content on Core Topics",
2524
+ effort: "High",
2525
+ 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.'
2526
+ },
2527
+ content_depth: {
2528
+ name: "Increase Content Depth",
2529
+ effort: "Medium",
2530
+ 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."
2492
2531
  }
2493
2532
  };
2494
2533
  function calculateImpact(score, weight, effort) {
@@ -2610,7 +2649,7 @@ function generatePitchNumbers(score, rawData, scorecard) {
2610
2649
  const passing = scorecard.filter((s) => s.score >= 7).length;
2611
2650
  metrics.push({
2612
2651
  metric: "Criteria Passing",
2613
- value: `${passing}/26`,
2652
+ value: `${passing}/28`,
2614
2653
  significance: passing >= 18 ? "Excellent coverage across AEO dimensions" : passing >= 12 ? "Good foundation with room to improve remaining criteria" : `${26 - passing} criteria need attention for full AI visibility`
2615
2654
  });
2616
2655
  return metrics;
@@ -2802,20 +2841,23 @@ async function fetchMultiPageData(siteData, options) {
2802
2841
 
2803
2842
  // src/page-scorer.ts
2804
2843
  var PAGE_CRITERIA = {
2805
- schema_markup: { weight: 0.15, label: "Schema.org Structured Data" },
2806
- qa_content_format: { weight: 0.15, label: "Q&A Content Format" },
2807
- clean_html: { weight: 0.1, label: "Clean, Crawlable HTML" },
2808
- faq_section: { weight: 0.1, label: "FAQ Section Content" },
2844
+ // Content Substance
2809
2845
  original_data: { weight: 0.1, label: "Original Data & Expert Content" },
2810
- query_answer_alignment: { weight: 0.08, label: "Query-Answer Alignment" },
2811
- content_freshness: { weight: 0.07, label: "Content Freshness Signals" },
2812
- table_list_extractability: { weight: 0.07, label: "Table & List Extractability" },
2813
- direct_answer_density: { weight: 0.07, label: "Direct Answer Paragraphs" },
2814
- semantic_html: { weight: 0.05, label: "Semantic HTML5 & Accessibility" },
2815
- fact_density: { weight: 0.05, label: "Fact & Data Density" },
2816
- definition_patterns: { weight: 0.04, label: "Definition Patterns" },
2817
- canonical_url: { weight: 0.04, label: "Canonical URL Strategy" },
2818
- visible_date_signal: { weight: 0.04, label: "Visible Date Signal" }
2846
+ fact_density: { weight: 0.06, label: "Fact & Data Density" },
2847
+ direct_answer_density: { weight: 0.05, label: "Direct Answer Paragraphs" },
2848
+ qa_content_format: { weight: 0.05, label: "Q&A Content Format" },
2849
+ query_answer_alignment: { weight: 0.05, label: "Query-Answer Alignment" },
2850
+ faq_section: { weight: 0.04, label: "FAQ Section Content" },
2851
+ // Content Organization
2852
+ content_freshness: { weight: 0.04, label: "Content Freshness Signals" },
2853
+ schema_markup: { weight: 0.03, label: "Schema.org Structured Data" },
2854
+ table_list_extractability: { weight: 0.03, label: "Table & List Extractability" },
2855
+ definition_patterns: { weight: 0.02, label: "Definition Patterns" },
2856
+ visible_date_signal: { weight: 0.02, label: "Visible Date Signal" },
2857
+ semantic_html: { weight: 0.02, label: "Semantic HTML5 & Accessibility" },
2858
+ clean_html: { weight: 0.02, label: "Clean, Crawlable HTML" },
2859
+ // Technical Plumbing
2860
+ canonical_url: { weight: 0.01, label: "Canonical URL Strategy" }
2819
2861
  };
2820
2862
  function extractJsonLdBlocks(html) {
2821
2863
  const blocks = [];
@@ -3667,32 +3709,37 @@ function buildLinkGraph(pages, domain, homepageUrl) {
3667
3709
 
3668
3710
  // src/fix-engine.ts
3669
3711
  var CRITERION_WEIGHTS2 = {
3670
- llms_txt: 0.1,
3671
- schema_markup: 0.15,
3672
- qa_content_format: 0.15,
3673
- clean_html: 0.1,
3674
- entity_consistency: 0.1,
3675
- robots_txt: 0.05,
3676
- faq_section: 0.1,
3712
+ // Content Substance (~55%)
3713
+ topic_coherence: 0.14,
3677
3714
  original_data: 0.1,
3678
- internal_linking: 0.1,
3679
- semantic_html: 0.05,
3680
- content_freshness: 0.07,
3681
- sitemap_completeness: 0.05,
3682
- rss_feed: 0.03,
3683
- table_list_extractability: 0.07,
3684
- definition_patterns: 0.04,
3685
- direct_answer_density: 0.07,
3686
- content_licensing: 0.04,
3687
- author_schema_depth: 0.04,
3688
- fact_density: 0.05,
3689
- canonical_url: 0.04,
3690
- content_velocity: 0.03,
3691
- schema_coverage: 0.03,
3692
- speakable_schema: 0.03,
3693
- query_answer_alignment: 0.08,
3694
- content_cannibalization: 0.05,
3695
- visible_date_signal: 0.04
3715
+ content_depth: 0.07,
3716
+ fact_density: 0.06,
3717
+ direct_answer_density: 0.05,
3718
+ qa_content_format: 0.05,
3719
+ query_answer_alignment: 0.05,
3720
+ faq_section: 0.04,
3721
+ // Content Organization (~30%)
3722
+ entity_consistency: 0.05,
3723
+ internal_linking: 0.04,
3724
+ content_freshness: 0.04,
3725
+ schema_markup: 0.03,
3726
+ author_schema_depth: 0.03,
3727
+ table_list_extractability: 0.03,
3728
+ definition_patterns: 0.02,
3729
+ visible_date_signal: 0.02,
3730
+ semantic_html: 0.02,
3731
+ clean_html: 0.02,
3732
+ // Technical Plumbing (~15%)
3733
+ content_cannibalization: 0.02,
3734
+ llms_txt: 0.02,
3735
+ robots_txt: 0.02,
3736
+ content_velocity: 0.02,
3737
+ content_licensing: 0.02,
3738
+ sitemap_completeness: 0.01,
3739
+ canonical_url: 0.01,
3740
+ rss_feed: 0.01,
3741
+ schema_coverage: 0.01,
3742
+ speakable_schema: 0.01
3696
3743
  };
3697
3744
  var PHASE_CONFIG = [
3698
3745
  {
@@ -3715,7 +3762,9 @@ var PHASE_CONFIG = [
3715
3762
  "content_freshness",
3716
3763
  "table_list_extractability",
3717
3764
  "query_answer_alignment",
3718
- "visible_date_signal"
3765
+ "visible_date_signal",
3766
+ "topic_coherence",
3767
+ "content_depth"
3719
3768
  ]
3720
3769
  },
3721
3770
  {
@@ -4619,6 +4668,55 @@ Summarization: yes`,
4619
4668
  affectedPages: affected,
4620
4669
  pageCount: affected?.length
4621
4670
  }];
4671
+ },
4672
+ topic_coherence: (c) => {
4673
+ if (c.score >= 10) return [];
4674
+ const impact = impactFromScore(c.score);
4675
+ const effort = effortForCriterion("topic_coherence", c.score);
4676
+ return [{
4677
+ id: "fix-topic-coherence",
4678
+ criterion: c.criterion_label,
4679
+ criterionId: c.criterion,
4680
+ title: "Focus blog content on core expertise",
4681
+ description: "Ensure blog content consistently covers your core topic areas. Scattered content across unrelated topics weakens AI engine authority signals.",
4682
+ impact,
4683
+ effort: effort === "trivial" ? "low" : effort,
4684
+ impactScore: 0,
4685
+ category: "content",
4686
+ steps: [
4687
+ "Identify 2-3 core expertise areas your brand is known for",
4688
+ "Audit existing blog posts and remove or consolidate off-topic content",
4689
+ "Create a content calendar focused on core topics",
4690
+ "Use topic clusters: pillar pages linking to supporting articles within the same niche"
4691
+ ],
4692
+ successCriteria: "80%+ of blog content covers core expertise areas with consistent topic focus"
4693
+ }];
4694
+ },
4695
+ content_depth: (c, pages) => {
4696
+ if (c.score >= 10) return [];
4697
+ const impact = impactFromScore(c.score);
4698
+ const effort = effortForCriterion("content_depth", c.score);
4699
+ const affected = getAffectedPages("content_depth", pages);
4700
+ return [{
4701
+ id: "fix-content-depth",
4702
+ criterion: c.criterion_label,
4703
+ criterionId: c.criterion,
4704
+ title: "Increase content depth and structure",
4705
+ description: "Expand thin content with more detail, examples, and structured sections. AI engines prefer comprehensive articles with clear heading hierarchies.",
4706
+ impact,
4707
+ effort: effort === "trivial" ? "low" : effort,
4708
+ impactScore: 0,
4709
+ category: "content",
4710
+ steps: [
4711
+ "Aim for 1000+ words per article with expert analysis and examples",
4712
+ "Use H2/H3 subheadings every 200-300 words for clear structure",
4713
+ "Add comparison tables, numbered steps, and data points",
4714
+ "Remove or expand thin pages (under 300 words) that dilute site quality"
4715
+ ],
4716
+ successCriteria: "Average article length exceeds 1000 words with 5+ subheadings per page",
4717
+ affectedPages: affected,
4718
+ pageCount: affected?.length
4719
+ }];
4622
4720
  }
4623
4721
  };
4624
4722
  function generateFixPlan(domain, overallScore, criteria, pagesReviewed, linkGraph) {