aeorank 1.4.0 → 1.6.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/README.md CHANGED
@@ -125,7 +125,7 @@ Run a complete audit. Returns `AuditResult` with:
125
125
  - `pitchNumbers` - Key metrics (schema types, AI crawler access, etc.)
126
126
  - `verdict` - Human-readable summary paragraph
127
127
  - `bottomLine` - Actionable recommendation
128
- - `pagesReviewed` - Per-page analysis with issues and strengths
128
+ - `pagesReviewed` - Per-page analysis with issues, strengths, and AEO score (0-100)
129
129
  - `elapsed` - Wall-clock seconds
130
130
 
131
131
  **Options:**
@@ -139,6 +139,66 @@ Run a complete audit. Returns `AuditResult` with:
139
139
  | `maxPages` | `number` | `200` | Max pages for full crawl |
140
140
  | `concurrency` | `number` | `5` | Parallel fetches for full crawl |
141
141
 
142
+ ### `scorePage(html, url?)`
143
+
144
+ Score a single HTML page against 14 per-page AEO criteria. Returns `PageScoreResult` with:
145
+
146
+ - `aeoScore` - 0-100 weighted score
147
+ - `criterionScores` - 14 `PageCriterionScore` entries (criterion, score 0-10, weight)
148
+
149
+ ### `scoreAllPages(siteData)`
150
+
151
+ Batch-score all pages (homepage + blogSample) from a `SiteData` object. Returns `PageScoreResult[]`.
152
+
153
+ ### `buildLinkGraph(pages, domain, homepageUrl)`
154
+
155
+ Analyze internal linking structure from crawled pages. Returns `LinkGraph` with:
156
+
157
+ - `nodes` - Map of URL to `PageNode` (in/out degree, depth, pillar/hub/orphan flags)
158
+ - `edges` - Array of `LinkEdge` (from, to, anchor text)
159
+ - `stats` - `LinkGraphStats` (total pages, orphans, pillars, hubs, avg depth, clusters)
160
+ - `clusters` - `TopicCluster[]` (pillar URL, spoke URLs, cohesion score)
161
+
162
+ ```ts
163
+ import { crawlFullSite, prefetchSiteData, buildLinkGraph } from 'aeorank';
164
+
165
+ const siteData = await prefetchSiteData('example.com');
166
+ const crawl = await crawlFullSite(siteData, { maxPages: 200 });
167
+ const graph = buildLinkGraph(crawl.pages, 'example.com', 'https://example.com');
168
+
169
+ console.log(graph.stats.orphanPages); // Pages with no inbound links
170
+ console.log(graph.stats.pillarPages); // High-authority hub pages
171
+ console.log(graph.clusters); // Topic clusters detected
172
+ ```
173
+
174
+ ### `generateFixPlan(domain, score, criteria, pages?, linkGraph?)`
175
+
176
+ Generate a phased fix plan from audit results. Returns `FixPlan` with:
177
+
178
+ - `phases` - 4 phases (Foundation, Content, Authority, Architecture) with prioritized `FixAction[]`
179
+ - `quickWins` - Low-effort, high-impact fixes
180
+ - `projectedScore` - Estimated score after applying all fixes
181
+ - `summary` - Counts by impact level, top opportunity, estimated effort
182
+
183
+ Each `FixAction` includes: title, description, impact/effort levels, step-by-step instructions, code examples, affected pages, and dependency ordering.
184
+
185
+ ```ts
186
+ import { audit, generateFixPlan } from 'aeorank';
187
+
188
+ const result = await audit('example.com');
189
+ const plan = generateFixPlan(
190
+ 'example.com',
191
+ result.overallScore,
192
+ result.criterionResults,
193
+ result.pagesReviewed,
194
+ );
195
+
196
+ console.log(plan.projectedScore); // e.g. 82
197
+ console.log(plan.quickWins[0].title); // e.g. "Add llms.txt file"
198
+ console.log(plan.quickWins[0].impactScore); // e.g. 10
199
+ console.log(plan.phases[0].fixes.length); // Foundation phase fixes
200
+ ```
201
+
142
202
  ### Advanced API
143
203
 
144
204
  For custom pipelines, import individual stages:
@@ -152,6 +212,10 @@ import {
152
212
  buildDetailedFindings,
153
213
  generateVerdict,
154
214
  generateOpportunities,
215
+ scorePage,
216
+ scoreAllPages,
217
+ buildLinkGraph,
218
+ generateFixPlan,
155
219
  isSpaShell,
156
220
  fetchWithHeadless,
157
221
  } from 'aeorank';
@@ -161,6 +225,24 @@ const results = auditSiteFromData(siteData);
161
225
  const score = calculateOverallScore(results);
162
226
  ```
163
227
 
228
+ ### Browser Entry Point
229
+
230
+ For browser environments (Chrome extensions, web apps), import from `aeorank/browser` to avoid Node.js dependencies (Puppeteer, fs):
231
+
232
+ ```ts
233
+ import {
234
+ prefetchSiteData,
235
+ auditSiteFromData,
236
+ calculateOverallScore,
237
+ buildLinkGraph,
238
+ generateFixPlan,
239
+ analyzeAllPages,
240
+ crawlFullSite,
241
+ } from 'aeorank/browser';
242
+ ```
243
+
244
+ The browser entry exports everything except `headless-fetch` (Puppeteer), `html-report` (Node fs), `audit` orchestrator, and CLI.
245
+
164
246
  ## SPA Support
165
247
 
166
248
  Sites that use client-side rendering (React, Vue, Angular) return empty HTML shells to regular HTTP requests. AEORank detects these automatically and re-renders them with Puppeteer if available.
@@ -208,6 +290,107 @@ console.log(crawlResult.pages.length); // Pages fetched
208
290
  console.log(crawlResult.discoveredUrls.length); // Total URLs found
209
291
  ```
210
292
 
293
+ ## Per-Page Scoring
294
+
295
+ AEORank scores each individual page (0-100) against the 14 criteria that apply at page level. Instead of only seeing "your site scores 62," you get "your /about page scores 45, your /blog/guide scores 78."
296
+
297
+ The 14 per-page criteria: Schema.org Structured Data, Q&A Content Format, Clean Crawlable HTML, FAQ Section Content, Original Data & Expert Content, Query-Answer Alignment, Content Freshness Signals, Table & List Extractability, Direct Answer Paragraphs, Semantic HTML5 & Accessibility, Fact & Data Density, Definition Patterns, Canonical URL Strategy, Visible Date Signal.
298
+
299
+ The remaining 12 criteria (llms.txt, robots.txt, sitemap, RSS, entity consistency, internal linking, content licensing, author schema, content velocity, schema coverage, speakable schema, content cannibalization) are site-level only.
300
+
301
+ ### CLI Output
302
+
303
+ Per-page scores appear in the pages section:
304
+
305
+ ```
306
+ Pages reviewed (47):
307
+ Homepage https://example.com 0 issues [AEO: 72]
308
+ Blog https://example.com/blog/post 2 issues [AEO: 58]
309
+
310
+ Average page AEO score: 62/100
311
+ Top: /blog/medicare-walkers-guide (92)
312
+ Bottom: /thank-you (23)
313
+ ```
314
+
315
+ ### Programmatic API
316
+
317
+ ```ts
318
+ import { scorePage, scoreAllPages } from 'aeorank';
319
+ import type { PageScoreResult, PageCriterionScore } from 'aeorank';
320
+
321
+ // Score a single page
322
+ const result = scorePage(html, url);
323
+ console.log(result.aeoScore); // 0-100
324
+ console.log(result.criterionScores); // 14 per-criterion scores
325
+
326
+ // Score all pages from site data
327
+ const allScores = scoreAllPages(siteData);
328
+ ```
329
+
330
+ ## Link Graph Analysis
331
+
332
+ Analyze your site's internal linking structure to find orphan pages, identify pillar content, and detect topic clusters:
333
+
334
+ ```bash
335
+ npx aeorank example.com --full-crawl --json | jq '.linkGraph.stats'
336
+ ```
337
+
338
+ ```ts
339
+ import { crawlFullSite, prefetchSiteData, buildLinkGraph, serializeLinkGraph } from 'aeorank';
340
+
341
+ const siteData = await prefetchSiteData('example.com');
342
+ const crawl = await crawlFullSite(siteData, { maxPages: 200 });
343
+ const graph = buildLinkGraph(crawl.pages, 'example.com', 'https://example.com');
344
+
345
+ // Orphan pages (no inbound links - invisible to crawlers)
346
+ const orphans = [...graph.nodes.values()].filter(n => n.isOrphan);
347
+
348
+ // Pillar pages (high authority, many inbound links)
349
+ const pillars = [...graph.nodes.values()].filter(n => n.isPillar);
350
+
351
+ // Topic clusters (pillar + spoke pages with high cohesion)
352
+ graph.clusters.forEach(c => {
353
+ console.log(`${c.pillarTitle}: ${c.spokes.length} spokes, cohesion ${c.cohesion}`);
354
+ });
355
+
356
+ // Serialize for storage/transport (Map -> plain object)
357
+ const json = serializeLinkGraph(graph);
358
+ ```
359
+
360
+ ## Fix Plan Engine
361
+
362
+ Generate actionable, phased fix plans from audit results. Each fix includes step-by-step instructions, code examples, effort/impact ratings, and dependency ordering:
363
+
364
+ ```bash
365
+ npx aeorank example.com --full-crawl --json | jq '.fixPlan'
366
+ ```
367
+
368
+ ```ts
369
+ import { audit, generateFixPlan } from 'aeorank';
370
+
371
+ const result = await audit('example.com', { fullCrawl: true });
372
+ const plan = generateFixPlan(
373
+ 'example.com',
374
+ result.overallScore,
375
+ result.criterionResults,
376
+ result.pagesReviewed,
377
+ result.linkGraph, // optional - enables link-aware fixes
378
+ );
379
+
380
+ // 4 phases: Foundation -> Content -> Authority -> Architecture
381
+ plan.phases.forEach(phase => {
382
+ console.log(`${phase.title}: ${phase.fixes.length} fixes`);
383
+ });
384
+
385
+ // Quick wins: low effort + high impact
386
+ plan.quickWins.forEach(qw => {
387
+ console.log(`${qw.title} (+${qw.impactScore} pts) - ${qw.effort} effort`);
388
+ qw.steps.forEach(s => console.log(` - ${s}`));
389
+ });
390
+
391
+ console.log(`Current: ${plan.overallScore} -> Projected: ${plan.projectedScore}`);
392
+ ```
393
+
211
394
  ## Scoring
212
395
 
213
396
  Each criterion is scored 0-10 by deterministic checks (regex, HTML parsing, HTTP headers). The overall score is a weighted average normalized to 0-100.
@@ -266,7 +449,7 @@ console.log(result.comparison.tied); // Criteria with equal scores
266
449
 
267
450
  ## Benchmark Dataset
268
451
 
269
- The `data/` directory contains the largest open dataset of AI visibility scores - **13,619 domains** scored across 23 criteria, including **4,328 Y Combinator startups** across 48 batches (W06-W26):
452
+ The `data/` directory contains the largest open dataset of AI visibility scores - **13,619 domains** scored across 26 criteria, including **4,328 Y Combinator startups** across 48 batches (W06-W26):
270
453
 
271
454
  | File | Contents |
272
455
  |------|----------|