@drowlink/aeo-linter-core 0.1.4 → 0.1.5

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.
Files changed (80) hide show
  1. package/README.md +8 -7
  2. package/dist/__tests__/assertions.test.d.ts +2 -0
  3. package/dist/__tests__/assertions.test.d.ts.map +1 -0
  4. package/dist/__tests__/assertions.test.js +89 -0
  5. package/dist/__tests__/assertions.test.js.map +1 -0
  6. package/dist/__tests__/audit-contract.test.js +32 -0
  7. package/dist/__tests__/audit-contract.test.js.map +1 -1
  8. package/dist/__tests__/author-eeat-presence.test.d.ts +2 -0
  9. package/dist/__tests__/author-eeat-presence.test.d.ts.map +1 -0
  10. package/dist/__tests__/author-eeat-presence.test.js +137 -0
  11. package/dist/__tests__/author-eeat-presence.test.js.map +1 -0
  12. package/dist/__tests__/fact-citation-density.test.d.ts +2 -0
  13. package/dist/__tests__/fact-citation-density.test.d.ts.map +1 -0
  14. package/dist/__tests__/fact-citation-density.test.js +100 -0
  15. package/dist/__tests__/fact-citation-density.test.js.map +1 -0
  16. package/dist/__tests__/llms-txt.test.d.ts +2 -0
  17. package/dist/__tests__/llms-txt.test.d.ts.map +1 -0
  18. package/dist/__tests__/llms-txt.test.js +108 -0
  19. package/dist/__tests__/llms-txt.test.js.map +1 -0
  20. package/dist/__tests__/runner.test.js +16 -3
  21. package/dist/__tests__/runner.test.js.map +1 -1
  22. package/dist/__tests__/table-list-scannability.test.d.ts +2 -0
  23. package/dist/__tests__/table-list-scannability.test.d.ts.map +1 -0
  24. package/dist/__tests__/table-list-scannability.test.js +97 -0
  25. package/dist/__tests__/table-list-scannability.test.js.map +1 -0
  26. package/dist/audits/ai-accessibility/ai-llms-txt.d.ts +10 -0
  27. package/dist/audits/ai-accessibility/ai-llms-txt.d.ts.map +1 -0
  28. package/dist/audits/ai-accessibility/ai-llms-txt.js +52 -0
  29. package/dist/audits/ai-accessibility/ai-llms-txt.js.map +1 -0
  30. package/dist/audits/content-chunking/table-list-scannability.d.ts +10 -0
  31. package/dist/audits/content-chunking/table-list-scannability.d.ts.map +1 -0
  32. package/dist/audits/content-chunking/table-list-scannability.js +79 -0
  33. package/dist/audits/content-chunking/table-list-scannability.js.map +1 -0
  34. package/dist/audits/direct-answer-density/fact-citation-density.d.ts +10 -0
  35. package/dist/audits/direct-answer-density/fact-citation-density.d.ts.map +1 -0
  36. package/dist/audits/direct-answer-density/fact-citation-density.js +73 -0
  37. package/dist/audits/direct-answer-density/fact-citation-density.js.map +1 -0
  38. package/dist/audits/index.d.ts +5 -1
  39. package/dist/audits/index.d.ts.map +1 -1
  40. package/dist/audits/index.js +9 -1
  41. package/dist/audits/index.js.map +1 -1
  42. package/dist/audits/structured-data/author-eeat-presence.d.ts +10 -0
  43. package/dist/audits/structured-data/author-eeat-presence.d.ts.map +1 -0
  44. package/dist/audits/structured-data/author-eeat-presence.js +87 -0
  45. package/dist/audits/structured-data/author-eeat-presence.js.map +1 -0
  46. package/dist/config/default-config.d.ts.map +1 -1
  47. package/dist/config/default-config.js +19 -11
  48. package/dist/config/default-config.js.map +1 -1
  49. package/dist/gather/gatherers/content-chunks-gatherer.d.ts.map +1 -1
  50. package/dist/gather/gatherers/content-chunks-gatherer.js +8 -0
  51. package/dist/gather/gatherers/content-chunks-gatherer.js.map +1 -1
  52. package/dist/gather/gatherers/direct-answers-gatherer.d.ts.map +1 -1
  53. package/dist/gather/gatherers/direct-answers-gatherer.js +31 -0
  54. package/dist/gather/gatherers/direct-answers-gatherer.js.map +1 -1
  55. package/dist/gather/gatherers/jsonld-gatherer.d.ts +1 -1
  56. package/dist/gather/gatherers/jsonld-gatherer.d.ts.map +1 -1
  57. package/dist/gather/gatherers/jsonld-gatherer.js +89 -6
  58. package/dist/gather/gatherers/jsonld-gatherer.js.map +1 -1
  59. package/dist/gather/gatherers/llms-txt-gatherer.d.ts +11 -0
  60. package/dist/gather/gatherers/llms-txt-gatherer.d.ts.map +1 -0
  61. package/dist/gather/gatherers/llms-txt-gatherer.js +123 -0
  62. package/dist/gather/gatherers/llms-txt-gatherer.js.map +1 -0
  63. package/dist/gather/index.d.ts +1 -0
  64. package/dist/gather/index.d.ts.map +1 -1
  65. package/dist/gather/index.js +1 -0
  66. package/dist/gather/index.js.map +1 -1
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +1 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/runner/assertions.d.ts +25 -0
  72. package/dist/runner/assertions.d.ts.map +1 -0
  73. package/dist/runner/assertions.js +53 -0
  74. package/dist/runner/assertions.js.map +1 -0
  75. package/dist/runner/runner.d.ts.map +1 -1
  76. package/dist/runner/runner.js +5 -1
  77. package/dist/runner/runner.js.map +1 -1
  78. package/dist/types/artifacts.d.ts +64 -0
  79. package/dist/types/artifacts.d.ts.map +1 -1
  80. package/package.json +1 -1
package/README.md CHANGED
@@ -51,19 +51,20 @@ main();
51
51
 
52
52
  ## 🏛️ Architecture (Lighthouse Pattern)
53
53
 
54
- - **Gatherers (`core/gather`)**: Extract raw typed artifacts (`RobotsTxt`, `JSONLD`, `HttpHeaders`, `HeadingsHierarchy`, `ContentChunks`, `DirectAnswers`).
55
- - **Audits (`core/audits`)**: Pure deterministic functions that evaluate artifacts and return `{ score, displayValue, details }`.
54
+ - **Gatherers (`core/gather`)**: Extract raw typed artifacts (`RobotsTxt`, `HttpHeaders`, `JSONLD`, `MetaTags`, `HeadingsHierarchy`, `ContentChunks`, `DirectAnswers`, `LlmsTxt`).
55
+ - **Audits (`core/audits`)**: 16 pure deterministic audit functions that evaluate artifacts and return typed `{ score, displayValue, details }`.
56
56
  - **Aggregator (`core/runner/aggregator`)**: Calculates weighted category scores and global AEO score (0–100).
57
+ - **Quality Gates (`core/runner/assertions`)**: `evaluateQualityGates` function for CI/CD threshold validation.
57
58
  - **Reporters (`core/report`)**: Terminal and interactive SVG gauge HTML dashboards.
58
59
 
59
60
  ---
60
61
 
61
- ## 🎯 Categories
62
+ ## 🎯 Categories & Audits (16 Audits)
62
63
 
63
- - `ai-accessibility`: Evaluates permissions for AI bots (`GPTBot`, `PerplexityBot`, `ClaudeBot`, `Google-Extended`, etc.) and `X-Robots-Tag`.
64
- - `structured-data`: Validates JSON-LD syntax and presence of RAG schemas (`FAQPage`, `HowTo`, `Article`, `sameAs`).
65
- - `content-chunking`: Verifies H1-H3 hierarchy, semantic tags (`<main>`, `<article>`, `<section>`), and embedding token density.
66
- - `direct-answer-density`: Measures conciseness (30–60 words) and direct definitions answering key user queries.
64
+ - `ai-accessibility` (25%): AI crawlers (`ai-robots-txt`), headers (`ai-x-robots-tag`), `/llms.txt` standard (`ai-llms-txt`), and Sitemaps (`ai-bot-sitemap`).
65
+ - `structured-data` (25%): RAG schemas (`rag-schema-presence`), JSON-LD syntax (`jsonld-syntax-validity`), Author E-E-A-T (`author-eeat-presence`), and Knowledge Graph (`entity-sameas-links`).
66
+ - `content-chunking` (25%): Headings (`heading-hierarchy`), HTML5 tags (`semantic-containers`), RAG token density (`chunk-token-density`), and tables/lists (`table-list-scannability`).
67
+ - `direct-answer-density` (25%): Definitions (`direct-definition-answering`), 30-60w conciseness (`concise-answer-wordcount`), facts & citations (`fact-citation-density`), and query headings (`question-heading-alignment`).
67
68
 
68
69
  ---
69
70
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=assertions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/assertions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { evaluateQualityGates } from '../runner/assertions.js';
3
+ describe('CI/CD Quality Gates Evaluator', () => {
4
+ const mockReport = {
5
+ url: 'https://example.com',
6
+ fetchTime: '2026-08-25T00:00:00Z',
7
+ aeoVersion: '0.1.4',
8
+ userAgent: 'test',
9
+ overallScore: 85,
10
+ categories: {
11
+ 'ai-accessibility': {
12
+ id: 'ai-accessibility',
13
+ title: 'AI Accessibility & Crawling',
14
+ description: '',
15
+ score: 1.0,
16
+ auditRefs: [],
17
+ },
18
+ 'structured-data': {
19
+ id: 'structured-data',
20
+ title: 'Structured Data & RAG Schemas',
21
+ description: '',
22
+ score: 0.75,
23
+ auditRefs: [],
24
+ },
25
+ 'content-chunking': {
26
+ id: 'content-chunking',
27
+ title: 'Content Chunking & Semantic Structure',
28
+ description: '',
29
+ score: 0.90,
30
+ auditRefs: [],
31
+ },
32
+ 'direct-answer-density': {
33
+ id: 'direct-answer-density',
34
+ title: 'Direct Answer Density & Fact Grounding',
35
+ description: '',
36
+ score: 0.70,
37
+ auditRefs: [],
38
+ },
39
+ },
40
+ audits: {},
41
+ };
42
+ it('passes when overall score is above failUnder threshold', () => {
43
+ const result = evaluateQualityGates(mockReport, { failUnder: 80 });
44
+ expect(result.passed).toBe(true);
45
+ expect(result.failures.length).toBe(0);
46
+ });
47
+ it('fails when overall score is below failUnder threshold', () => {
48
+ const result = evaluateQualityGates(mockReport, { failUnder: 90 });
49
+ expect(result.passed).toBe(false);
50
+ expect(result.failures.length).toBe(1);
51
+ expect(result.failures[0]?.target).toBe('overall');
52
+ expect(result.failures[0]?.actual).toBe(85);
53
+ expect(result.failures[0]?.expected).toBe(90);
54
+ });
55
+ it('evaluates category assertions successfully when all thresholds are met', () => {
56
+ const result = evaluateQualityGates(mockReport, {
57
+ categoryAssertions: {
58
+ 'ai-accessibility': 95,
59
+ 'content-chunking': 80,
60
+ },
61
+ });
62
+ expect(result.passed).toBe(true);
63
+ expect(result.failures.length).toBe(0);
64
+ });
65
+ it('detects category failures and formats expected vs actual scores', () => {
66
+ const result = evaluateQualityGates(mockReport, {
67
+ categoryAssertions: {
68
+ 'structured-data': 90, // actual is 75
69
+ 'direct-answer-density': 80, // actual is 70
70
+ },
71
+ });
72
+ expect(result.passed).toBe(false);
73
+ expect(result.failures.length).toBe(2);
74
+ expect(result.failures[0]?.name).toBe('Structured Data & RAG Schemas');
75
+ expect(result.failures[0]?.actual).toBe(75);
76
+ expect(result.failures[0]?.expected).toBe(90);
77
+ });
78
+ it('flags missing/unknown categories as assertion failures', () => {
79
+ const result = evaluateQualityGates(mockReport, {
80
+ categoryAssertions: {
81
+ 'unknown-category': 50,
82
+ },
83
+ });
84
+ expect(result.passed).toBe(false);
85
+ expect(result.failures[0]?.name).toBe('unknown-category');
86
+ expect(result.failures[0]?.actual).toBe(0);
87
+ });
88
+ });
89
+ //# sourceMappingURL=assertions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.test.js","sourceRoot":"","sources":["../../src/__tests__/assertions.test.ts"],"names":[],"mappings":"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"}
@@ -71,6 +71,16 @@ describe('AEO Linter Core Contracts (Lighthouse Architecture)', () => {
71
71
  hasProduct: false,
72
72
  hasSameAs: false,
73
73
  sameAsUrls: [],
74
+ authorEeat: {
75
+ hasAuthorSchema: false,
76
+ authorSameAsUrls: [],
77
+ hasJobTitle: false,
78
+ hasPublisherSchema: false,
79
+ hasDatePublished: false,
80
+ hasDateModified: false,
81
+ hasDomAuthorByline: false,
82
+ hasDomPublishedDate: false,
83
+ },
74
84
  },
75
85
  MetaTags: {
76
86
  title: 'AEO Example',
@@ -100,12 +110,34 @@ describe('AEO Linter Core Contracts (Lighthouse Architecture)', () => {
100
110
  hasSemanticMain: true,
101
111
  hasSemanticArticle: false,
102
112
  hasSemanticSections: false,
113
+ totalTablesCount: 0,
114
+ totalListsCount: 0,
115
+ totalListItemsCount: 0,
116
+ structuredTablesCount: 0,
103
117
  },
104
118
  DirectAnswers: {
105
119
  pairs: [],
106
120
  directAnswerRatio: 0,
107
121
  conciseAnswersCount: 0,
108
122
  definitionPatternsFound: 0,
123
+ facts: {
124
+ percentagesCount: 0,
125
+ numericalMetricsCount: 0,
126
+ citationPhrasesCount: 0,
127
+ externalSourcesCount: 0,
128
+ externalSourcesUrls: [],
129
+ totalFactSignals: 0,
130
+ },
131
+ },
132
+ LlmsTxt: {
133
+ exists: true,
134
+ statusCode: 200,
135
+ rawContent: '# Test',
136
+ charCount: 6,
137
+ hasFullVersion: false,
138
+ fullStatusCode: 404,
139
+ sections: [],
140
+ totalDeclaredLinks: 0,
109
141
  },
110
142
  };
111
143
  const result = await MockRobotsAudit.audit(mockArtifacts);
@@ -1 +1 @@
1
- {"version":3,"file":"audit-contract.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-contract.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit-contract.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-contract.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=author-eeat-presence.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-eeat-presence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/author-eeat-presence.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CheerioDriver } from '../gather/driver/cheerio-driver.js';
3
+ import { JSONLDGatherer } from '../gather/gatherers/jsonld-gatherer.js';
4
+ import { AuthorEeatPresenceAudit } from '../audits/structured-data/author-eeat-presence.js';
5
+ describe('Author E-E-A-T Presence Audit', () => {
6
+ it('JSONLDGatherer extracts structured author, publisher, and DOM bylines', async () => {
7
+ const html = `
8
+ <!DOCTYPE html>
9
+ <html>
10
+ <head>
11
+ <script type="application/ld+json">
12
+ {
13
+ "@context": "https://schema.org",
14
+ "@type": "Article",
15
+ "headline": "AEO Architectural Blueprint",
16
+ "datePublished": "2026-01-15T08:00:00Z",
17
+ "dateModified": "2026-02-20T10:00:00Z",
18
+ "author": {
19
+ "@type": "Person",
20
+ "name": "Dr. Jane Doe",
21
+ "jobTitle": "Principal AI Researcher",
22
+ "sameAs": [
23
+ "https://www.linkedin.com/in/janedoe",
24
+ "https://twitter.com/janedoe"
25
+ ]
26
+ },
27
+ "publisher": {
28
+ "@type": "Organization",
29
+ "name": "AI Research Labs"
30
+ }
31
+ }
32
+ </script>
33
+ </head>
34
+ <body>
35
+ <main>
36
+ <h1>AEO Architectural Blueprint</h1>
37
+ <p class="author-name">By Dr. Jane Doe</p>
38
+ <time datetime="2026-01-15">January 15, 2026</time>
39
+ </main>
40
+ </body>
41
+ </html>
42
+ `;
43
+ const mockDriver = new CheerioDriver({
44
+ url: 'https://example.com/article',
45
+ html,
46
+ });
47
+ const gatherer = new JSONLDGatherer();
48
+ const artifact = await gatherer.getArtifact({
49
+ url: 'https://example.com/article',
50
+ driver: mockDriver,
51
+ });
52
+ expect(artifact.authorEeat.hasAuthorSchema).toBe(true);
53
+ expect(artifact.authorEeat.authorName).toBe('Dr. Jane Doe');
54
+ expect(artifact.authorEeat.authorType).toBe('Person');
55
+ expect(artifact.authorEeat.hasJobTitle).toBe(true);
56
+ expect(artifact.authorEeat.authorSameAsUrls).toContain('https://www.linkedin.com/in/janedoe');
57
+ expect(artifact.authorEeat.hasPublisherSchema).toBe(true);
58
+ expect(artifact.authorEeat.publisherName).toBe('AI Research Labs');
59
+ expect(artifact.authorEeat.hasDatePublished).toBe(true);
60
+ expect(artifact.authorEeat.hasDomAuthorByline).toBe(true);
61
+ expect(artifact.authorEeat.domAuthorText).toContain('Dr. Jane Doe');
62
+ });
63
+ it('AuthorEeatPresenceAudit scores 1.0 for verified authors with sameAs profiles and publisher', async () => {
64
+ const mockArtifacts = {
65
+ JSONLD: {
66
+ hasOrganization: true,
67
+ authorEeat: {
68
+ hasAuthorSchema: true,
69
+ authorName: 'Dr. Jane Doe',
70
+ authorType: 'Person',
71
+ authorSameAsUrls: ['https://linkedin.com/in/janedoe'],
72
+ hasPublisherSchema: true,
73
+ publisherName: 'AI Labs',
74
+ hasDatePublished: true,
75
+ datePublished: '2026-01-01',
76
+ hasDateModified: true,
77
+ dateModified: '2026-02-01',
78
+ hasDomAuthorByline: true,
79
+ domAuthorText: 'By Dr. Jane Doe',
80
+ hasDomPublishedDate: true,
81
+ },
82
+ },
83
+ ContentChunks: {
84
+ totalWordCount: 500,
85
+ },
86
+ };
87
+ const result = await AuthorEeatPresenceAudit.audit(mockArtifacts);
88
+ expect(result.score).toBe(1);
89
+ expect(result.displayValue).toContain('Author verified: Dr. Jane Doe');
90
+ expect(result.details?.type).toBe('table');
91
+ });
92
+ it('AuthorEeatPresenceAudit penalizes long articles (>400 words) with zero author/publisher credentials', async () => {
93
+ const mockArtifacts = {
94
+ JSONLD: {
95
+ hasOrganization: false,
96
+ authorEeat: {
97
+ hasAuthorSchema: false,
98
+ authorSameAsUrls: [],
99
+ hasPublisherSchema: false,
100
+ hasDatePublished: false,
101
+ hasDateModified: false,
102
+ hasDomAuthorByline: false,
103
+ hasDomPublishedDate: false,
104
+ },
105
+ },
106
+ ContentChunks: {
107
+ totalWordCount: 650,
108
+ },
109
+ };
110
+ const result = await AuthorEeatPresenceAudit.audit(mockArtifacts);
111
+ expect(result.score).toBe(0.2);
112
+ expect(result.explanation).toContain('Article/Person schema with author name');
113
+ });
114
+ it('AuthorEeatPresenceAudit is lenient on short pages (<150 words)', async () => {
115
+ const mockArtifacts = {
116
+ JSONLD: {
117
+ hasOrganization: false,
118
+ authorEeat: {
119
+ hasAuthorSchema: false,
120
+ authorSameAsUrls: [],
121
+ hasPublisherSchema: false,
122
+ hasDatePublished: false,
123
+ hasDateModified: false,
124
+ hasDomAuthorByline: false,
125
+ hasDomPublishedDate: false,
126
+ },
127
+ },
128
+ ContentChunks: {
129
+ totalWordCount: 90,
130
+ },
131
+ };
132
+ const result = await AuthorEeatPresenceAudit.audit(mockArtifacts);
133
+ expect(result.score).toBe(1);
134
+ expect(result.displayValue).toContain('Brief page');
135
+ });
136
+ });
137
+ //# sourceMappingURL=author-eeat-presence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-eeat-presence.test.js","sourceRoot":"","sources":["../../src/__tests__/author-eeat-presence.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fact-citation-density.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-citation-density.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fact-citation-density.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CheerioDriver } from '../gather/driver/cheerio-driver.js';
3
+ import { DirectAnswersGatherer } from '../gather/gatherers/direct-answers-gatherer.js';
4
+ import { FactCitationDensityAudit } from '../audits/direct-answer-density/fact-citation-density.js';
5
+ describe('Fact and Citation Density Audit (GEO)', () => {
6
+ it('DirectAnswersGatherer detects percentages, metrics, citations and external links', async () => {
7
+ const html = `
8
+ <!DOCTYPE html>
9
+ <html>
10
+ <body>
11
+ <main>
12
+ <h1>State of Generative Engine Optimization</h1>
13
+ <h2>What does recent research indicate?</h2>
14
+ <p>According to a study by Princeton researchers, optimizing for GEO increases citation rates by 40% across major LLMs with over 500k queries tested in 2025.</p>
15
+ <p>For more details, see the official paper on <a href="https://arxiv.org/abs/2311.09735">arXiv</a> and <a href="https://github.com/georesearch">GitHub</a>.</p>
16
+ </main>
17
+ </body>
18
+ </html>
19
+ `;
20
+ const mockDriver = new CheerioDriver({
21
+ url: 'https://example.com/geo-study',
22
+ html,
23
+ });
24
+ const gatherer = new DirectAnswersGatherer();
25
+ const artifact = await gatherer.getArtifact({
26
+ url: 'https://example.com/geo-study',
27
+ driver: mockDriver,
28
+ });
29
+ expect(artifact.facts.percentagesCount).toBeGreaterThanOrEqual(1);
30
+ expect(artifact.facts.citationPhrasesCount).toBeGreaterThanOrEqual(1);
31
+ expect(artifact.facts.externalSourcesCount).toBe(2);
32
+ expect(artifact.facts.externalSourcesUrls).toContain('https://arxiv.org');
33
+ expect(artifact.facts.totalFactSignals).toBeGreaterThanOrEqual(4);
34
+ });
35
+ it('FactCitationDensityAudit scores 1.0 with comprehensive facts and external links', async () => {
36
+ const mockArtifacts = {
37
+ DirectAnswers: {
38
+ pairs: [],
39
+ facts: {
40
+ percentagesCount: 2,
41
+ numericalMetricsCount: 2,
42
+ citationPhrasesCount: 1,
43
+ externalSourcesCount: 1,
44
+ externalSourcesUrls: ['https://arxiv.org'],
45
+ totalFactSignals: 6,
46
+ },
47
+ },
48
+ ContentChunks: {
49
+ totalWordCount: 350,
50
+ },
51
+ };
52
+ const result = await FactCitationDensityAudit.audit(mockArtifacts);
53
+ expect(result.score).toBe(1);
54
+ expect(result.displayValue).toContain('6 fact signal(s) found');
55
+ expect(result.details?.type).toBe('table');
56
+ });
57
+ it('FactCitationDensityAudit penalizes dense articles (>400 words) with zero facts or sources', async () => {
58
+ const mockArtifacts = {
59
+ DirectAnswers: {
60
+ pairs: [],
61
+ facts: {
62
+ percentagesCount: 0,
63
+ numericalMetricsCount: 0,
64
+ citationPhrasesCount: 0,
65
+ externalSourcesCount: 0,
66
+ externalSourcesUrls: [],
67
+ totalFactSignals: 0,
68
+ },
69
+ },
70
+ ContentChunks: {
71
+ totalWordCount: 700,
72
+ },
73
+ };
74
+ const result = await FactCitationDensityAudit.audit(mockArtifacts);
75
+ expect(result.score).toBe(0.3);
76
+ expect(result.explanation).toContain('verifiable percentages');
77
+ });
78
+ it('FactCitationDensityAudit allows short pages (<150 words)', async () => {
79
+ const mockArtifacts = {
80
+ DirectAnswers: {
81
+ pairs: [],
82
+ facts: {
83
+ percentagesCount: 0,
84
+ numericalMetricsCount: 0,
85
+ citationPhrasesCount: 0,
86
+ externalSourcesCount: 0,
87
+ externalSourcesUrls: [],
88
+ totalFactSignals: 0,
89
+ },
90
+ },
91
+ ContentChunks: {
92
+ totalWordCount: 80,
93
+ },
94
+ };
95
+ const result = await FactCitationDensityAudit.audit(mockArtifacts);
96
+ expect(result.score).toBe(1);
97
+ expect(result.displayValue).toContain('Brief page');
98
+ });
99
+ });
100
+ //# sourceMappingURL=fact-citation-density.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-citation-density.test.js","sourceRoot":"","sources":["../../src/__tests__/fact-citation-density.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=llms-txt.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/llms-txt.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CheerioDriver } from '../gather/driver/cheerio-driver.js';
3
+ import { LlmsTxtGatherer } from '../gather/gatherers/llms-txt-gatherer.js';
4
+ import { AiLlmsTxtAudit } from '../audits/ai-accessibility/ai-llms-txt.js';
5
+ describe('LLMS.txt Standard Gatherer & Audit', () => {
6
+ it('LlmsTxtGatherer parses title, summary, sections and links correctly', async () => {
7
+ const mockLlmsContent = `# FastHTML
8
+ > The fastest way to build web apps in pure Python.
9
+
10
+ ## Core Documentation
11
+ - [Quickstart](https://fastht.ml/docs/quickstart.html): Get started in 5 minutes
12
+ - [Components](https://fastht.ml/docs/components.html): Comprehensive components guide
13
+
14
+ ## Optional
15
+ - [API Reference](https://fastht.ml/docs/api.html)
16
+ `;
17
+ const mockDriver = new CheerioDriver({
18
+ url: 'https://fastht.ml/docs',
19
+ html: '<html><body>Docs</body></html>',
20
+ });
21
+ mockDriver.fetch = async (url) => {
22
+ if (url.endsWith('/llms.txt')) {
23
+ return new Response(mockLlmsContent, {
24
+ status: 200,
25
+ headers: { 'content-type': 'text/markdown' },
26
+ });
27
+ }
28
+ if (url.endsWith('/llms-full.txt')) {
29
+ return new Response('# Full documentation\nAll text in one file.', {
30
+ status: 200,
31
+ headers: { 'content-type': 'text/plain' },
32
+ });
33
+ }
34
+ return new Response('Not found', { status: 404 });
35
+ };
36
+ const gatherer = new LlmsTxtGatherer();
37
+ const artifact = await gatherer.getArtifact({
38
+ url: 'https://fastht.ml/docs',
39
+ driver: mockDriver,
40
+ });
41
+ expect(artifact.exists).toBe(true);
42
+ expect(artifact.title).toBe('FastHTML');
43
+ expect(artifact.summary).toBe('The fastest way to build web apps in pure Python.');
44
+ expect(artifact.hasFullVersion).toBe(true);
45
+ expect(artifact.totalDeclaredLinks).toBe(3);
46
+ expect(artifact.sections.length).toBe(2);
47
+ expect(artifact.sections[0]?.title).toBe('Core Documentation');
48
+ expect(artifact.sections[0]?.links[0]?.title).toBe('Quickstart');
49
+ expect(artifact.sections[0]?.links[0]?.description).toBe('Get started in 5 minutes');
50
+ });
51
+ it('LlmsTxtGatherer rejects HTML 404 error page disguised as 200', async () => {
52
+ const mockHtml404 = `<!DOCTYPE html><html><head><title>Not Found</title></head><body><h1>404 Not Found</h1></body></html>`;
53
+ const mockDriver = new CheerioDriver({
54
+ url: 'https://spa-example.com',
55
+ html: '<html><body>App</body></html>',
56
+ });
57
+ mockDriver.fetch = async () => new Response(mockHtml404, {
58
+ status: 200,
59
+ headers: { 'content-type': 'text/html' },
60
+ });
61
+ const gatherer = new LlmsTxtGatherer();
62
+ const artifact = await gatherer.getArtifact({
63
+ url: 'https://spa-example.com',
64
+ driver: mockDriver,
65
+ });
66
+ expect(artifact.exists).toBe(false);
67
+ expect(artifact.rawContent).toBeNull();
68
+ });
69
+ it('AiLlmsTxtAudit scores 1.0 when valid llms.txt and full version are present', async () => {
70
+ const mockArtifacts = {
71
+ LlmsTxt: {
72
+ exists: true,
73
+ statusCode: 200,
74
+ rawContent: '# My Project\n> Summary\n- [Link](https://example.com)',
75
+ charCount: 50,
76
+ hasFullVersion: true,
77
+ fullStatusCode: 200,
78
+ title: 'My Project',
79
+ summary: 'Summary',
80
+ sections: [{ title: 'Main', links: [{ title: 'Link', url: 'https://example.com' }] }],
81
+ totalDeclaredLinks: 1,
82
+ },
83
+ };
84
+ const result = await AiLlmsTxtAudit.audit(mockArtifacts);
85
+ expect(result.score).toBe(1);
86
+ expect(result.displayValue).toContain('active');
87
+ expect(result.details?.type).toBe('table');
88
+ });
89
+ it('AiLlmsTxtAudit scores 0 with actionable advice when missing', async () => {
90
+ const mockArtifacts = {
91
+ LlmsTxt: {
92
+ exists: false,
93
+ statusCode: 404,
94
+ rawContent: null,
95
+ charCount: 0,
96
+ hasFullVersion: false,
97
+ fullStatusCode: 404,
98
+ sections: [],
99
+ totalDeclaredLinks: 0,
100
+ },
101
+ };
102
+ const result = await AiLlmsTxtAudit.audit(mockArtifacts);
103
+ expect(result.score).toBe(0);
104
+ expect(result.displayValue).toBe('No /llms.txt file found');
105
+ expect(result.explanation).toContain('Create an /llms.txt file at your site root');
106
+ });
107
+ });
108
+ //# sourceMappingURL=llms-txt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt.test.js","sourceRoot":"","sources":["../../src/__tests__/llms-txt.test.ts"],"names":[],"mappings":"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"}
@@ -52,9 +52,22 @@ describe('Runner and Aggregator Full Pipeline', () => {
52
52
  url: 'https://aeo-example.com/test',
53
53
  html: mockHtml,
54
54
  });
55
- mockDriver.fetch = async () => new Response('User-agent: GPTBot\nAllow: /\nSitemap: https://aeo-example.com/sitemap.xml', {
56
- status: 200,
57
- });
55
+ mockDriver.fetch = async (url) => {
56
+ if (url.endsWith('/robots.txt')) {
57
+ return new Response('User-agent: GPTBot\nAllow: /\nSitemap: https://aeo-example.com/sitemap.xml', {
58
+ status: 200,
59
+ });
60
+ }
61
+ if (url.endsWith('/llms.txt')) {
62
+ return new Response('# Complete Guide\n> Comprehensive AI Guide\n- [Guide](https://aeo-example.com/guide): Docs', {
63
+ status: 200,
64
+ });
65
+ }
66
+ if (url.endsWith('/llms-full.txt')) {
67
+ return new Response('# Full docs', { status: 200 });
68
+ }
69
+ return new Response('Not found', { status: 404 });
70
+ };
58
71
  const report = await Runner.run('https://aeo-example.com/test', {
59
72
  driver: mockDriver,
60
73
  });