@drowlink/aeo-linter-core 0.1.1 → 0.1.4
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/__tests__/audit-contract.test.js +12 -13
- package/dist/__tests__/audit-contract.test.js.map +1 -1
- package/dist/__tests__/gatherers.test.js +11 -12
- package/dist/__tests__/gatherers.test.js.map +1 -1
- package/dist/__tests__/runner.test.js +12 -12
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/audits/ai-accessibility/ai-bot-sitemap.d.ts +1 -1
- package/dist/audits/ai-accessibility/ai-bot-sitemap.js +6 -6
- package/dist/audits/ai-accessibility/ai-bot-sitemap.js.map +1 -1
- package/dist/audits/ai-accessibility/ai-robots-txt.d.ts +1 -1
- package/dist/audits/ai-accessibility/ai-robots-txt.d.ts.map +1 -1
- package/dist/audits/ai-accessibility/ai-robots-txt.js +11 -12
- package/dist/audits/ai-accessibility/ai-robots-txt.js.map +1 -1
- package/dist/audits/ai-accessibility/ai-x-robots-tag.d.ts +1 -1
- package/dist/audits/ai-accessibility/ai-x-robots-tag.js +8 -8
- package/dist/audits/ai-accessibility/ai-x-robots-tag.js.map +1 -1
- package/dist/audits/audit.d.ts +13 -13
- package/dist/audits/audit.js +14 -14
- package/dist/audits/audit.js.map +1 -1
- package/dist/audits/content-chunking/chunk-token-density.d.ts +1 -1
- package/dist/audits/content-chunking/chunk-token-density.js +12 -12
- package/dist/audits/content-chunking/chunk-token-density.js.map +1 -1
- package/dist/audits/content-chunking/heading-hierarchy.d.ts +1 -1
- package/dist/audits/content-chunking/heading-hierarchy.js +13 -13
- package/dist/audits/content-chunking/heading-hierarchy.js.map +1 -1
- package/dist/audits/content-chunking/semantic-containers.d.ts +1 -1
- package/dist/audits/content-chunking/semantic-containers.js +7 -7
- package/dist/audits/content-chunking/semantic-containers.js.map +1 -1
- package/dist/audits/direct-answer-density/concise-answer-wordcount.d.ts +1 -1
- package/dist/audits/direct-answer-density/concise-answer-wordcount.js +11 -11
- package/dist/audits/direct-answer-density/concise-answer-wordcount.js.map +1 -1
- package/dist/audits/direct-answer-density/direct-definition-answering.d.ts +1 -1
- package/dist/audits/direct-answer-density/direct-definition-answering.js +12 -12
- package/dist/audits/direct-answer-density/direct-definition-answering.js.map +1 -1
- package/dist/audits/direct-answer-density/question-heading-alignment.d.ts +1 -1
- package/dist/audits/direct-answer-density/question-heading-alignment.js +7 -7
- package/dist/audits/direct-answer-density/question-heading-alignment.js.map +1 -1
- package/dist/audits/index.d.ts +1 -1
- package/dist/audits/index.js +1 -1
- package/dist/audits/structured-data/entity-sameas-links.d.ts +1 -1
- package/dist/audits/structured-data/entity-sameas-links.js +11 -11
- package/dist/audits/structured-data/entity-sameas-links.js.map +1 -1
- package/dist/audits/structured-data/jsonld-syntax-validity.d.ts +1 -1
- package/dist/audits/structured-data/jsonld-syntax-validity.js +12 -12
- package/dist/audits/structured-data/jsonld-syntax-validity.js.map +1 -1
- package/dist/audits/structured-data/rag-schema-presence.d.ts +1 -1
- package/dist/audits/structured-data/rag-schema-presence.js +13 -13
- package/dist/audits/structured-data/rag-schema-presence.js.map +1 -1
- package/dist/config/default-config.d.ts +2 -2
- package/dist/config/default-config.js +6 -6
- package/dist/config/default-config.js.map +1 -1
- package/dist/gather/driver/cheerio-driver.d.ts +1 -1
- package/dist/gather/driver/cheerio-driver.js +4 -4
- package/dist/gather/driver/cheerio-driver.js.map +1 -1
- package/dist/gather/gatherers/content-chunks-gatherer.d.ts +1 -1
- package/dist/gather/gatherers/content-chunks-gatherer.d.ts.map +1 -1
- package/dist/gather/gatherers/content-chunks-gatherer.js +4 -6
- package/dist/gather/gatherers/content-chunks-gatherer.js.map +1 -1
- package/dist/gather/gatherers/direct-answers-gatherer.d.ts +1 -1
- package/dist/gather/gatherers/direct-answers-gatherer.d.ts.map +1 -1
- package/dist/gather/gatherers/direct-answers-gatherer.js +4 -5
- package/dist/gather/gatherers/direct-answers-gatherer.js.map +1 -1
- package/dist/gather/gatherers/headings-hierarchy-gatherer.d.ts +1 -1
- package/dist/gather/gatherers/headings-hierarchy-gatherer.js +2 -2
- package/dist/gather/gatherers/headings-hierarchy-gatherer.js.map +1 -1
- package/dist/gather/gatherers/robots-txt-gatherer.d.ts +1 -1
- package/dist/gather/gatherers/robots-txt-gatherer.d.ts.map +1 -1
- package/dist/gather/gatherers/robots-txt-gatherer.js +2 -4
- package/dist/gather/gatherers/robots-txt-gatherer.js.map +1 -1
- package/dist/report/html-reporter.d.ts +2 -2
- package/dist/report/html-reporter.js +9 -9
- package/dist/report/html-reporter.js.map +1 -1
- package/dist/report/terminal-reporter.d.ts +1 -1
- package/dist/report/terminal-reporter.js +3 -3
- package/dist/report/terminal-reporter.js.map +1 -1
- package/dist/runner/aggregator.d.ts +3 -3
- package/dist/runner/aggregator.js +5 -5
- package/dist/runner/aggregator.js.map +1 -1
- package/dist/runner/runner.d.ts +7 -7
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +17 -18
- package/dist/runner/runner.js.map +1 -1
- package/dist/types/artifacts.d.ts +19 -21
- package/dist/types/artifacts.d.ts.map +1 -1
- package/dist/types/artifacts.js +3 -3
- package/dist/types/audit.d.ts +29 -29
- package/dist/types/audit.d.ts.map +1 -1
- package/dist/types/audit.js +2 -2
- package/dist/types/config.d.ts +21 -21
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +1 -1
- package/dist/types/gatherer.d.ts +6 -6
- package/dist/types/gatherer.d.ts.map +1 -1
- package/dist/types/gatherer.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating text passage length and token density for RAG embeddings
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating text passage length and token density for RAG embeddings
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class ChunkTokenDensityAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'chunk-token-density',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Content chunks maintain optimal token density for RAG (150 - 500 tokens)',
|
|
9
|
+
failureTitle: 'Content chunks are excessively long or fragmented',
|
|
10
|
+
description: 'Most RAG embedding models (OpenAI text-embedding-3, Cohere Embed, Voyage) achieve highest retrieval precision when passages contain 150–500 tokens grouped under a topical heading.',
|
|
11
11
|
requiredArtifacts: ['ContentChunks'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -16,27 +16,27 @@ export class ChunkTokenDensityAudit extends Audit {
|
|
|
16
16
|
if (chunks.length === 0) {
|
|
17
17
|
return this.generateAuditResult({
|
|
18
18
|
score: 0.5,
|
|
19
|
-
displayValue: 'No
|
|
19
|
+
displayValue: 'No substantial text chunks found',
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
const optimalChunks = chunks.filter((c) => c.estimatedTokens >= 100 && c.estimatedTokens <= 600);
|
|
23
23
|
const ratio = optimalChunks.length / chunks.length;
|
|
24
24
|
const tableItems = chunks.slice(0, 10).map((c) => ({
|
|
25
25
|
id: c.id,
|
|
26
|
-
heading: c.headingText || '(
|
|
26
|
+
heading: c.headingText || '(No heading)',
|
|
27
27
|
words: c.wordCount,
|
|
28
28
|
tokens: `~${c.estimatedTokens}`,
|
|
29
|
-
status: c.estimatedTokens >= 100 && c.estimatedTokens <= 600 ? '
|
|
29
|
+
status: c.estimatedTokens >= 100 && c.estimatedTokens <= 600 ? 'Optimal' : c.estimatedTokens < 100 ? 'Too short' : 'Too long',
|
|
30
30
|
}));
|
|
31
31
|
return this.generateAuditResult({
|
|
32
32
|
score: ratio >= 0.7 ? 1 : Math.max(0.4, ratio),
|
|
33
|
-
displayValue: `${optimalChunks.length}
|
|
33
|
+
displayValue: `${optimalChunks.length} of ${chunks.length} chunks have optimal size (Average: ~${contentChunks.averageChunkTokenCount} tokens)`,
|
|
34
34
|
details: this.makeTableDetails([
|
|
35
35
|
{ key: 'id', label: 'ID', valueType: 'code' },
|
|
36
|
-
{ key: 'heading', label: '
|
|
37
|
-
{ key: 'words', label: '
|
|
38
|
-
{ key: 'tokens', label: 'Tokens
|
|
39
|
-
{ key: 'status', label: '
|
|
36
|
+
{ key: 'heading', label: 'Associated Heading', valueType: 'text' },
|
|
37
|
+
{ key: 'words', label: 'Words', valueType: 'numeric' },
|
|
38
|
+
{ key: 'tokens', label: 'Estimated Tokens', valueType: 'numeric' },
|
|
39
|
+
{ key: 'status', label: 'Diagnostic', valueType: 'status' },
|
|
40
40
|
], tableItems),
|
|
41
41
|
});
|
|
42
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-token-density.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/chunk-token-density.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACxC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"chunk-token-density.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/chunk-token-density.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACxC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,0EAA0E;QACjF,YAAY,EAAE,mDAAmD;QACjE,WAAW,EACT,qLAAqL;QACvL,iBAAiB,EAAE,CAAC,eAAe,CAAC;KACrC,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC;QAC9C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC;QAEpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,mBAAmB,CAAC;gBAC9B,KAAK,EAAE,GAAG;gBACV,YAAY,EAAE,kCAAkC;aACjD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,GAAG,IAAI,CAAC,CAAC,eAAe,IAAI,GAAG,CAAC,CAAC;QACjG,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAEnD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,OAAO,EAAE,CAAC,CAAC,WAAW,IAAI,cAAc;YACxC,KAAK,EAAE,CAAC,CAAC,SAAS;YAClB,MAAM,EAAE,IAAI,CAAC,CAAC,eAAe,EAAE;YAC/B,MAAM,EAAE,CAAC,CAAC,eAAe,IAAI,GAAG,IAAI,CAAC,CAAC,eAAe,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU;SAC9H,CAAC,CAAC,CAAC;QAEJ,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;YAC9C,YAAY,EAAE,GAAG,aAAa,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,wCAAwC,aAAa,CAAC,sBAAsB,UAAU;YAC/I,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAC5B;gBACE,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC7C,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,EAAE;gBAClE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE;gBACtD,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,SAAS,EAAE;gBAClE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE;aAC5D,EACD,UAAU,CACX;SACF,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit validating sequential heading hierarchy (H1-H6) and single H1 tag
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit validating sequential heading hierarchy (H1-H6) and single H1 tag
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class HeadingHierarchyAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'heading-hierarchy',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Heading structure (H1-H6) is sequential and contains a single H1',
|
|
9
|
+
failureTitle: 'Heading hierarchy contains level jumps or multiple H1 tags',
|
|
10
|
+
description: 'A clear, sequential hierarchy (H1 -> H2 -> H3) allows RAG chunking algorithms to partition documents with accurate semantic context.',
|
|
11
11
|
requiredArtifacts: ['HeadingsHierarchy'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -15,33 +15,33 @@ export class HeadingHierarchyAudit extends Audit {
|
|
|
15
15
|
if (hierarchy.headings.length === 0) {
|
|
16
16
|
return this.generateAuditResult({
|
|
17
17
|
score: 0,
|
|
18
|
-
displayValue: 'No
|
|
19
|
-
explanation: '
|
|
18
|
+
displayValue: 'No headings found (H1-H6)',
|
|
19
|
+
explanation: 'Document lacks semantic headings to structure the content.',
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
let deductions = 0;
|
|
23
23
|
const issues = [];
|
|
24
24
|
if (hierarchy.h1Count === 0) {
|
|
25
25
|
deductions += 0.4;
|
|
26
|
-
issues.push('
|
|
26
|
+
issues.push('Missing main H1 heading');
|
|
27
27
|
}
|
|
28
28
|
else if (hierarchy.h1Count > 1) {
|
|
29
29
|
deductions += 0.2;
|
|
30
|
-
issues.push(`
|
|
30
|
+
issues.push(`Found ${hierarchy.h1Count} H1 tags (exactly one is recommended)`);
|
|
31
31
|
}
|
|
32
32
|
if (!hierarchy.isHierarchySequential) {
|
|
33
33
|
deductions += 0.3;
|
|
34
|
-
issues.push(`
|
|
34
|
+
issues.push(`Found ${hierarchy.skippedLevels.length} skipped heading levels (e.g. H1 to H3)`);
|
|
35
35
|
}
|
|
36
36
|
const score = this.clampScore(1 - deductions);
|
|
37
37
|
return this.generateAuditResult({
|
|
38
38
|
score,
|
|
39
|
-
displayValue: issues.length === 0 ? '
|
|
39
|
+
displayValue: issues.length === 0 ? 'Optimal sequential heading hierarchy' : issues.join(', '),
|
|
40
40
|
details: hierarchy.skippedLevels.length > 0
|
|
41
41
|
? this.makeTableDetails([
|
|
42
|
-
{ key: 'from', label: '
|
|
43
|
-
{ key: 'to', label: '
|
|
44
|
-
{ key: 'text', label: '
|
|
42
|
+
{ key: 'from', label: 'From Level', valueType: 'code' },
|
|
43
|
+
{ key: 'to', label: 'To Level', valueType: 'code' },
|
|
44
|
+
{ key: 'text', label: 'Heading Text', valueType: 'text' },
|
|
45
45
|
], hierarchy.skippedLevels.map((s) => ({
|
|
46
46
|
from: `H${s.from}`,
|
|
47
47
|
to: `H${s.to}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heading-hierarchy.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/heading-hierarchy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACvC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"heading-hierarchy.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/heading-hierarchy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACvC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,kEAAkE;QACzE,YAAY,EAAE,4DAA4D;QAC1E,WAAW,EACT,sIAAsI;QACxI,iBAAiB,EAAE,CAAC,mBAAmB,CAAC;KACzC,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,SAAS,GAAG,SAAS,CAAC,iBAAiB,CAAC;QAE9C,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,mBAAmB,CAAC;gBAC9B,KAAK,EAAE,CAAC;gBACR,YAAY,EAAE,2BAA2B;gBACzC,WAAW,EAAE,4DAA4D;aAC1E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,IAAI,SAAS,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YAC5B,UAAU,IAAI,GAAG,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,SAAS,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACjC,UAAU,IAAI,GAAG,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,OAAO,uCAAuC,CAAC,CAAC;QACjF,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC;YACrC,UAAU,IAAI,GAAG,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,aAAa,CAAC,MAAM,yCAAyC,CAAC,CAAC;QAChG,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9F,OAAO,EAAE,SAAS,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;gBACzC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CACnB;oBACE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE;oBACvD,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE;oBACnD,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE;iBAC1D,EACD,SAAS,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBAClC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE;oBAClB,EAAE,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE;oBACd,IAAI,EAAE,CAAC,CAAC,IAAI;iBACb,CAAC,CAAC,CACJ;gBACH,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating semantic HTML5 container elements (<main>, <article>, <section>)
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating semantic HTML5 container elements (<main>, <article>, <section>)
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class SemanticContainersAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'semantic-containers',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Content uses semantic HTML5 containers (<main>, <article>, <section>)',
|
|
9
|
+
failureTitle: 'Content relies on generic <div> wrappers without semantic HTML5 markup',
|
|
10
|
+
description: 'Semantic tags like <main>, <article>, and <section> isolate primary content from boilerplate (nav, sidebar, footer), improving chunk clean extraction for AI parsers.',
|
|
11
11
|
requiredArtifacts: ['ContentChunks'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -21,12 +21,12 @@ export class SemanticContainersAudit extends Audit {
|
|
|
21
21
|
else if (hasMain || hasArticleOrSection) {
|
|
22
22
|
score = 0.7;
|
|
23
23
|
}
|
|
24
|
-
const tags = chunks.semanticTagsUsed.length > 0 ? chunks.semanticTagsUsed.join(', ') : '
|
|
24
|
+
const tags = chunks.semanticTagsUsed.length > 0 ? chunks.semanticTagsUsed.join(', ') : 'None (<div/body>)';
|
|
25
25
|
return this.generateAuditResult({
|
|
26
26
|
score,
|
|
27
|
-
displayValue: `
|
|
27
|
+
displayValue: `Semantic tags detected: ${tags}`,
|
|
28
28
|
explanation: score < 1
|
|
29
|
-
? '
|
|
29
|
+
? 'We recommend wrapping primary content within <main> and partitioning with <article> or <section>.'
|
|
30
30
|
: undefined,
|
|
31
31
|
});
|
|
32
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-containers.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/semantic-containers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACzC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"semantic-containers.js","sourceRoot":"","sources":["../../../src/audits/content-chunking/semantic-containers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACzC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,uEAAuE;QAC9E,YAAY,EAAE,wEAAwE;QACtF,WAAW,EACT,uKAAuK;QACzK,iBAAiB,EAAE,CAAC,eAAe,CAAC;KACrC,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC;QAEvC,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC;QACvC,MAAM,mBAAmB,GAAG,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,mBAAmB,CAAC;QAEpF,IAAI,KAAK,GAAG,GAAG,CAAC;QAChB,IAAI,OAAO,IAAI,mBAAmB,EAAE,CAAC;YACnC,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;aAAM,IAAI,OAAO,IAAI,mBAAmB,EAAE,CAAC;YAC1C,KAAK,GAAG,GAAG,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC;QAE3G,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,2BAA2B,IAAI,EAAE;YAC/C,WAAW,EAAE,KAAK,GAAG,CAAC;gBACpB,CAAC,CAAC,mGAAmG;gBACrG,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating direct answer conciseness (ideal 30 to 60 words)
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating direct answer conciseness (ideal 30 to 60 words)
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class ConciseAnswerWordCountAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'concise-answer-wordcount',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Direct answers maintain concise length (30 - 60 words)',
|
|
9
|
+
failureTitle: 'Direct answers are overly verbose or too brief',
|
|
10
|
+
description: 'Language models prefer answers between 30 and 60 words as initial featured snippets for synthesis.',
|
|
11
11
|
requiredArtifacts: ['DirectAnswers'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -16,7 +16,7 @@ export class ConciseAnswerWordCountAudit extends Audit {
|
|
|
16
16
|
if (pairs.length === 0) {
|
|
17
17
|
return this.generateAuditResult({
|
|
18
18
|
score: 0.5,
|
|
19
|
-
displayValue: '
|
|
19
|
+
displayValue: 'No questions detected',
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
const conciseCount = directAnswers.conciseAnswersCount;
|
|
@@ -24,17 +24,17 @@ export class ConciseAnswerWordCountAudit extends Audit {
|
|
|
24
24
|
const score = ratio >= 0.6 ? 1 : Math.max(0.3, ratio);
|
|
25
25
|
return this.generateAuditResult({
|
|
26
26
|
score,
|
|
27
|
-
displayValue: `${conciseCount}
|
|
27
|
+
displayValue: `${conciseCount} of ${pairs.length} answers have concise length (30-60 words)`,
|
|
28
28
|
numericValue: conciseCount,
|
|
29
|
-
numericUnit: '
|
|
29
|
+
numericUnit: 'answers',
|
|
30
30
|
details: this.makeTableDetails([
|
|
31
|
-
{ key: 'question', label: '
|
|
32
|
-
{ key: 'words', label: '
|
|
33
|
-
{ key: 'status', label: '
|
|
31
|
+
{ key: 'question', label: 'Question', valueType: 'text' },
|
|
32
|
+
{ key: 'words', label: 'Word Count', valueType: 'numeric' },
|
|
33
|
+
{ key: 'status', label: 'Evaluation', valueType: 'status' },
|
|
34
34
|
], pairs.map((p) => ({
|
|
35
35
|
question: p.question,
|
|
36
36
|
words: p.answerWordCount,
|
|
37
|
-
status: p.isConcise ? '
|
|
37
|
+
status: p.isConcise ? 'Concise (30-60 words)' : p.answerWordCount < 20 ? 'Too brief' : 'Verbose (>75 words)',
|
|
38
38
|
}))),
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concise-answer-wordcount.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/concise-answer-wordcount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAC7C,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,0BAA0B;QAC9B,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"concise-answer-wordcount.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/concise-answer-wordcount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAC7C,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,0BAA0B;QAC9B,KAAK,EAAE,wDAAwD;QAC/D,YAAY,EAAE,gDAAgD;QAC9D,WAAW,EACT,oGAAoG;QACtG,iBAAiB,EAAE,CAAC,eAAe,CAAC;KACrC,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC;QAC9C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC;QAElC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,mBAAmB,CAAC;gBAC9B,KAAK,EAAE,GAAG;gBACV,YAAY,EAAE,uBAAuB;aACtC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,YAAY,GAAG,aAAa,CAAC,mBAAmB,CAAC;QACvD,MAAM,KAAK,GAAG,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1C,MAAM,KAAK,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEtD,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,GAAG,YAAY,OAAO,KAAK,CAAC,MAAM,4CAA4C;YAC5F,YAAY,EAAE,YAAY;YAC1B,WAAW,EAAE,SAAS;YACtB,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAC5B;gBACE,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE;gBACzD,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE;gBAC3D,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE;aAC5D,EACD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,KAAK,EAAE,CAAC,CAAC,eAAe;gBACxB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,qBAAqB;aAC7G,CAAC,CAAC,CACJ;SACF,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating presence of direct definitions answering key questions
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating presence of direct definitions answering key questions
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class DirectDefinitionAnsweringAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'direct-definition-answering',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Key questions are answered directly with clear definitions in the opening paragraph',
|
|
9
|
+
failureTitle: 'Missing direct answers or clear definitions following key questions',
|
|
10
|
+
description: 'Answer Engines (Perplexity, SearchGPT, Google AI Overviews) prioritize passages that deliver a direct answer in the opening lines (e.g. "X is a...", "The process involves..."), rather than introductory filler.',
|
|
11
11
|
requiredArtifacts: ['DirectAnswers'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -16,8 +16,8 @@ export class DirectDefinitionAnsweringAudit extends Audit {
|
|
|
16
16
|
if (pairs.length === 0) {
|
|
17
17
|
return this.generateAuditResult({
|
|
18
18
|
score: 0.5,
|
|
19
|
-
displayValue: 'No
|
|
20
|
-
explanation: '
|
|
19
|
+
displayValue: 'No questions detected in headings to evaluate direct answers',
|
|
20
|
+
explanation: 'Structuring headings as questions (e.g. "What is X?") improves snippet extraction in Answer Engines.',
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
const definitionPairs = pairs.filter((p) => p.hasDirectDefinition);
|
|
@@ -25,17 +25,17 @@ export class DirectDefinitionAnsweringAudit extends Audit {
|
|
|
25
25
|
const tableItems = pairs.map((p) => ({
|
|
26
26
|
question: p.question,
|
|
27
27
|
words: p.answerWordCount,
|
|
28
|
-
hasDef: p.hasDirectDefinition ? '
|
|
28
|
+
hasDef: p.hasDirectDefinition ? 'Yes (Clear definition)' : 'No (Indirect / descriptive)',
|
|
29
29
|
preview: p.answerText.length > 80 ? p.answerText.slice(0, 80) + '...' : p.answerText,
|
|
30
30
|
}));
|
|
31
31
|
return this.generateAuditResult({
|
|
32
32
|
score: definitionPairs.length > 0 ? (score >= 0.5 ? 1 : 0.7) : 0.4,
|
|
33
|
-
displayValue: `${definitionPairs.length}
|
|
33
|
+
displayValue: `${definitionPairs.length} of ${pairs.length} questions provide immediate direct definitions`,
|
|
34
34
|
details: this.makeTableDetails([
|
|
35
|
-
{ key: 'question', label: '
|
|
36
|
-
{ key: 'hasDef', label: '
|
|
37
|
-
{ key: 'words', label: '
|
|
38
|
-
{ key: 'preview', label: '
|
|
35
|
+
{ key: 'question', label: 'Detected Question', valueType: 'text' },
|
|
36
|
+
{ key: 'hasDef', label: 'Answer Pattern', valueType: 'status' },
|
|
37
|
+
{ key: 'words', label: 'Words', valueType: 'numeric' },
|
|
38
|
+
{ key: 'preview', label: 'Answer Extract', valueType: 'text' },
|
|
39
39
|
], tableItems),
|
|
40
40
|
});
|
|
41
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"direct-definition-answering.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/direct-definition-answering.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IAChD,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,6BAA6B;QACjC,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"direct-definition-answering.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/direct-definition-answering.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IAChD,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,6BAA6B;QACjC,KAAK,EAAE,qFAAqF;QAC5F,YAAY,EAAE,qEAAqE;QACnF,WAAW,EACT,mNAAmN;QACrN,iBAAiB,EAAE,CAAC,eAAe,CAAC;KACrC,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC;QAC9C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC;QAElC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,mBAAmB,CAAC;gBAC9B,KAAK,EAAE,GAAG;gBACV,YAAY,EAAE,8DAA8D;gBAC5E,WAAW,EAAE,sGAAsG;aACpH,CAAC,CAAC;QACL,CAAC;QAED,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAEnE,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,KAAK,EAAE,CAAC,CAAC,eAAe;YACxB,MAAM,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,6BAA6B;YACxF,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;SACrF,CAAC,CAAC,CAAC;QAEJ,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK,EAAE,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG;YAClE,YAAY,EAAE,GAAG,eAAe,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,iDAAiD;YAC3G,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAC5B;gBACE,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,EAAE;gBAClE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,QAAQ,EAAE;gBAC/D,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE;gBACtD,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE;aAC/D,EACD,UAAU,CACX;SACF,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating question-formulated search intent subheadings
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating question-formulated search intent subheadings
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class QuestionHeadingAlignmentAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'question-heading-alignment',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Headings are structured as explicit questions or search queries',
|
|
9
|
+
failureTitle: 'Lacks subheadings phrased as search queries or FAQs',
|
|
10
|
+
description: 'Formulating subheadings (H2/H3) as natural questions (e.g. "How does X work?", "Why choose Y?") significantly boosts semantic match in vector search and RAG.',
|
|
11
11
|
requiredArtifacts: ['HeadingsHierarchy', 'DirectAnswers'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -18,16 +18,16 @@ export class QuestionHeadingAlignmentAudit extends Audit {
|
|
|
18
18
|
if (totalSubheadings === 0) {
|
|
19
19
|
return this.generateAuditResult({
|
|
20
20
|
score: 0.5,
|
|
21
|
-
displayValue: 'No
|
|
21
|
+
displayValue: 'No H2 or H3 subheadings found',
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
const ratio = questionHeadings / Math.max(1, totalSubheadings);
|
|
25
25
|
const score = questionHeadings >= 1 ? (ratio >= 0.25 ? 1 : 0.8) : 0.4;
|
|
26
26
|
return this.generateAuditResult({
|
|
27
27
|
score,
|
|
28
|
-
displayValue: `${questionHeadings}
|
|
28
|
+
displayValue: `${questionHeadings} of ${totalSubheadings} subheadings phrased as questions`,
|
|
29
29
|
explanation: questionHeadings === 0
|
|
30
|
-
? '
|
|
30
|
+
? 'We recommend phrasing at least 1 or 2 H2/H3 subheadings as direct user questions.'
|
|
31
31
|
: undefined,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-heading-alignment.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/question-heading-alignment.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC/C,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,4BAA4B;QAChC,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"question-heading-alignment.js","sourceRoot":"","sources":["../../../src/audits/direct-answer-density/question-heading-alignment.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC/C,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,4BAA4B;QAChC,KAAK,EAAE,iEAAiE;QACxE,YAAY,EAAE,qDAAqD;QACnE,WAAW,EACT,+JAA+J;QACjK,iBAAiB,EAAE,CAAC,mBAAmB,EAAE,eAAe,CAAC;KAC1D,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,QAAQ,GAAG,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QACtD,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC;QAE9C,MAAM,gBAAgB,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;QACpD,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAEvF,IAAI,gBAAgB,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,mBAAmB,CAAC;gBAC9B,KAAK,EAAE,GAAG;gBACV,YAAY,EAAE,+BAA+B;aAC9C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,KAAK,GAAG,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAEtE,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,GAAG,gBAAgB,OAAO,gBAAgB,mCAAmC;YAC3F,WAAW,EAAE,gBAAgB,KAAK,CAAC;gBACjC,CAAC,CAAC,mFAAmF;gBACrF,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC"}
|
package/dist/audits/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Central registry and exports of all available AEO audits
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from './audit.js';
|
|
5
5
|
import { AiRobotsTxtAudit } from './ai-accessibility/ai-robots-txt.js';
|
package/dist/audits/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating entity disambiguation sameAs links for Knowledge Graphs
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit evaluating entity disambiguation sameAs links for Knowledge Graphs
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class EntitySameAsLinksAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'entity-sameas-links',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: 'No
|
|
10
|
-
description: '
|
|
8
|
+
title: 'Defines sameAs properties for Knowledge Graph entity disambiguation',
|
|
9
|
+
failureTitle: 'No sameAs links found in structured data',
|
|
10
|
+
description: 'The `sameAs` property (linking to Wikidata, Wikipedia, Crunchbase, or official profiles) connects your brand and topics to verified entities in LLM Knowledge Graphs.',
|
|
11
11
|
requiredArtifacts: ['JSONLD'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -16,13 +16,13 @@ export class EntitySameAsLinksAudit extends Audit {
|
|
|
16
16
|
const hasSameAs = sameAsUrls.length > 0;
|
|
17
17
|
const score = hasSameAs ? 1 : 0.4;
|
|
18
18
|
const tableItems = sameAsUrls.map((url) => {
|
|
19
|
-
let authority = '
|
|
19
|
+
let authority = 'External Authority Link';
|
|
20
20
|
if (url.includes('wikidata.org'))
|
|
21
|
-
authority = 'Wikidata (
|
|
21
|
+
authority = 'Wikidata (High Confidence)';
|
|
22
22
|
else if (url.includes('wikipedia.org'))
|
|
23
|
-
authority = 'Wikipedia (
|
|
23
|
+
authority = 'Wikipedia (High Confidence)';
|
|
24
24
|
else if (url.includes('linkedin.com') || url.includes('twitter.com') || url.includes('x.com'))
|
|
25
|
-
authority = '
|
|
25
|
+
authority = 'Official Social Profile';
|
|
26
26
|
return {
|
|
27
27
|
url,
|
|
28
28
|
type: authority,
|
|
@@ -30,11 +30,11 @@ export class EntitySameAsLinksAudit extends Audit {
|
|
|
30
30
|
});
|
|
31
31
|
return this.generateAuditResult({
|
|
32
32
|
score,
|
|
33
|
-
displayValue: hasSameAs ? `${sameAsUrls.length}
|
|
33
|
+
displayValue: hasSameAs ? `${sameAsUrls.length} sameAs link(s) found` : 'No sameAs links found',
|
|
34
34
|
details: hasSameAs
|
|
35
35
|
? this.makeTableDetails([
|
|
36
|
-
{ key: 'url', label: 'URL
|
|
37
|
-
{ key: 'type', label: '
|
|
36
|
+
{ key: 'url', label: 'sameAs URL', valueType: 'url' },
|
|
37
|
+
{ key: 'type', label: 'Entity Type', valueType: 'text' },
|
|
38
38
|
], tableItems)
|
|
39
39
|
: undefined,
|
|
40
40
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-sameas-links.js","sourceRoot":"","sources":["../../../src/audits/structured-data/entity-sameas-links.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACxC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"entity-sameas-links.js","sourceRoot":"","sources":["../../../src/audits/structured-data/entity-sameas-links.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACxC,MAAM,CAAU,IAAI,GAAc;QACvC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,qEAAqE;QAC5E,YAAY,EAAE,0CAA0C;QACxD,WAAW,EACT,uKAAuK;QACzK,iBAAiB,EAAE,CAAC,QAAQ,CAAC;KAC9B,CAAC;IAEK,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,SAAoB;QACrD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAChC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAErC,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAElC,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACxC,IAAI,SAAS,GAAG,yBAAyB,CAAC;YAC1C,IAAI,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC;gBAAE,SAAS,GAAG,4BAA4B,CAAC;iBACtE,IAAI,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE,SAAS,GAAG,6BAA6B,CAAC;iBAC7E,IAAI,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS,GAAG,yBAAyB,CAAC;YAErI,OAAO;gBACL,GAAG;gBACH,IAAI,EAAE,SAAS;aAChB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,uBAAuB,CAAC,CAAC,CAAC,uBAAuB;YAC/F,OAAO,EAAE,SAAS;gBAChB,CAAC,CAAC,IAAI,CAAC,gBAAgB,CACnB;oBACE,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE;oBACrD,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE;iBACzD,EACD,UAAU,CACX;gBACH,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit validating the syntax and integrity of JSON-LD schema blocks
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
import type { Artifacts, AuditMeta, AuditResult } from '../../types/index.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Audit validating the syntax and integrity of JSON-LD schema blocks
|
|
3
3
|
*/
|
|
4
4
|
import { Audit } from '../audit.js';
|
|
5
5
|
export class JsonLdSyntaxValidityAudit extends Audit {
|
|
6
6
|
static meta = {
|
|
7
7
|
id: 'jsonld-syntax-validity',
|
|
8
|
-
title: '
|
|
9
|
-
failureTitle: '
|
|
10
|
-
description: '
|
|
8
|
+
title: 'All JSON-LD blocks have valid, error-free syntax',
|
|
9
|
+
failureTitle: 'Syntax errors detected in JSON-LD structured data blocks',
|
|
10
|
+
description: 'Malformed JSON prevents Answer Engine parsers and RAG pipelines from extracting structured entity information.',
|
|
11
11
|
requiredArtifacts: ['JSONLD'],
|
|
12
12
|
};
|
|
13
13
|
static async audit(artifacts) {
|
|
@@ -15,8 +15,8 @@ export class JsonLdSyntaxValidityAudit extends Audit {
|
|
|
15
15
|
if (jsonld.items.length === 0) {
|
|
16
16
|
return this.generateAuditResult({
|
|
17
17
|
score: 0.5,
|
|
18
|
-
displayValue: 'No
|
|
19
|
-
explanation: '
|
|
18
|
+
displayValue: 'No JSON-LD structured data blocks found on page',
|
|
19
|
+
explanation: 'Adding Schema.org structured data via <script type="application/ld+json"> is recommended.',
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
const invalidItems = jsonld.items.filter((item) => !item.isValid);
|
|
@@ -24,18 +24,18 @@ export class JsonLdSyntaxValidityAudit extends Audit {
|
|
|
24
24
|
const score = validCount / jsonld.items.length;
|
|
25
25
|
const tableItems = jsonld.items.map((item, idx) => ({
|
|
26
26
|
index: idx + 1,
|
|
27
|
-
type: item.type ? (Array.isArray(item.type) ? item.type.join(', ') : item.type) : '
|
|
28
|
-
status: item.isValid ? '
|
|
29
|
-
error: item.syntaxErrors ? item.syntaxErrors.join('; ') : '
|
|
27
|
+
type: item.type ? (Array.isArray(item.type) ? item.type.join(', ') : item.type) : 'Unknown',
|
|
28
|
+
status: item.isValid ? 'Valid' : 'Syntax Error',
|
|
29
|
+
error: item.syntaxErrors ? item.syntaxErrors.join('; ') : 'None',
|
|
30
30
|
}));
|
|
31
31
|
return this.generateAuditResult({
|
|
32
32
|
score,
|
|
33
|
-
displayValue: `${validCount}
|
|
33
|
+
displayValue: `${validCount} of ${jsonld.items.length} JSON-LD blocks are valid`,
|
|
34
34
|
details: this.makeTableDetails([
|
|
35
35
|
{ key: 'index', label: '#', valueType: 'numeric' },
|
|
36
36
|
{ key: 'type', label: '@type', valueType: 'code' },
|
|
37
|
-
{ key: 'status', label: '
|
|
38
|
-
{ key: 'error', label: '
|
|
37
|
+
{ key: 'status', label: 'Status', valueType: 'status' },
|
|
38
|
+
{ key: 'error', label: 'Detail', valueType: 'text' },
|
|
39
39
|
], tableItems),
|
|
40
40
|
});
|
|
41
41
|
}
|