ax-audit 4.2.1 → 6.0.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/CHANGELOG.md +19 -0
- package/README.md +39 -4
- package/dist/baseline.js +1 -1
- package/dist/baseline.js.map +1 -1
- package/dist/check-ids.js +1 -1
- package/dist/check-ids.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +1 -85
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -135
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/license.d.ts +8 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +18 -0
- package/dist/license.js.map +1 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +205 -0
- package/dist/metadata.js.map +1 -0
- package/dist/orchestrator.d.ts +1 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +134 -56
- package/dist/orchestrator.js.map +1 -1
- package/dist/scorer.js +2 -2
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/api.md +14 -3
- package/docs/architecture.md +9 -98
- package/docs/ci.md +19 -0
- package/docs/cli.md +11 -0
- package/docs/faq.md +18 -0
- package/docs/getting-started.md +33 -1
- package/package.json +5 -5
- package/dist/checks/agent-access.d.ts +0 -33
- package/dist/checks/agent-access.d.ts.map +0 -1
- package/dist/checks/agent-access.js +0 -256
- package/dist/checks/agent-access.js.map +0 -1
- package/dist/checks/agent-card.d.ts +0 -37
- package/dist/checks/agent-card.d.ts.map +0 -1
- package/dist/checks/agent-card.js +0 -352
- package/dist/checks/agent-card.js.map +0 -1
- package/dist/checks/agent-operability.d.ts +0 -66
- package/dist/checks/agent-operability.d.ts.map +0 -1
- package/dist/checks/agent-operability.js +0 -383
- package/dist/checks/agent-operability.js.map +0 -1
- package/dist/checks/agent-skills.d.ts +0 -24
- package/dist/checks/agent-skills.d.ts.map +0 -1
- package/dist/checks/agent-skills.js +0 -316
- package/dist/checks/agent-skills.js.map +0 -1
- package/dist/checks/ai-catalog.d.ts +0 -28
- package/dist/checks/ai-catalog.d.ts.map +0 -1
- package/dist/checks/ai-catalog.js +0 -254
- package/dist/checks/ai-catalog.js.map +0 -1
- package/dist/checks/ai-directives.d.ts +0 -57
- package/dist/checks/ai-directives.d.ts.map +0 -1
- package/dist/checks/ai-directives.js +0 -263
- package/dist/checks/ai-directives.js.map +0 -1
- package/dist/checks/api-discovery.d.ts +0 -26
- package/dist/checks/api-discovery.d.ts.map +0 -1
- package/dist/checks/api-discovery.js +0 -432
- package/dist/checks/api-discovery.js.map +0 -1
- package/dist/checks/auth-discovery.d.ts +0 -28
- package/dist/checks/auth-discovery.d.ts.map +0 -1
- package/dist/checks/auth-discovery.js +0 -302
- package/dist/checks/auth-discovery.js.map +0 -1
- package/dist/checks/commerce-discovery.d.ts +0 -40
- package/dist/checks/commerce-discovery.d.ts.map +0 -1
- package/dist/checks/commerce-discovery.js +0 -295
- package/dist/checks/commerce-discovery.js.map +0 -1
- package/dist/checks/content-negotiation.d.ts +0 -4
- package/dist/checks/content-negotiation.d.ts.map +0 -1
- package/dist/checks/content-negotiation.js +0 -253
- package/dist/checks/content-negotiation.js.map +0 -1
- package/dist/checks/crawl-efficiency.d.ts +0 -16
- package/dist/checks/crawl-efficiency.d.ts.map +0 -1
- package/dist/checks/crawl-efficiency.js +0 -186
- package/dist/checks/crawl-efficiency.js.map +0 -1
- package/dist/checks/frontmatter.d.ts +0 -34
- package/dist/checks/frontmatter.d.ts.map +0 -1
- package/dist/checks/frontmatter.js +0 -100
- package/dist/checks/frontmatter.js.map +0 -1
- package/dist/checks/html-rendering.d.ts +0 -21
- package/dist/checks/html-rendering.d.ts.map +0 -1
- package/dist/checks/html-rendering.js +0 -221
- package/dist/checks/html-rendering.js.map +0 -1
- package/dist/checks/html-utils.d.ts +0 -66
- package/dist/checks/html-utils.d.ts.map +0 -1
- package/dist/checks/html-utils.js +0 -139
- package/dist/checks/html-utils.js.map +0 -1
- package/dist/checks/http-headers.d.ts +0 -11
- package/dist/checks/http-headers.d.ts.map +0 -1
- package/dist/checks/http-headers.js +0 -249
- package/dist/checks/http-headers.js.map +0 -1
- package/dist/checks/http-hygiene.d.ts +0 -26
- package/dist/checks/http-hygiene.d.ts.map +0 -1
- package/dist/checks/http-hygiene.js +0 -257
- package/dist/checks/http-hygiene.js.map +0 -1
- package/dist/checks/index.d.ts +0 -3
- package/dist/checks/index.d.ts.map +0 -1
- package/dist/checks/index.js +0 -55
- package/dist/checks/index.js.map +0 -1
- package/dist/checks/llms-txt.d.ts +0 -19
- package/dist/checks/llms-txt.d.ts.map +0 -1
- package/dist/checks/llms-txt.js +0 -281
- package/dist/checks/llms-txt.js.map +0 -1
- package/dist/checks/mcp-discovery.d.ts +0 -30
- package/dist/checks/mcp-discovery.d.ts.map +0 -1
- package/dist/checks/mcp-discovery.js +0 -523
- package/dist/checks/mcp-discovery.js.map +0 -1
- package/dist/checks/meta-tags.d.ts +0 -17
- package/dist/checks/meta-tags.d.ts.map +0 -1
- package/dist/checks/meta-tags.js +0 -188
- package/dist/checks/meta-tags.js.map +0 -1
- package/dist/checks/robots-parser.d.ts +0 -110
- package/dist/checks/robots-parser.d.ts.map +0 -1
- package/dist/checks/robots-parser.js +0 -277
- package/dist/checks/robots-parser.js.map +0 -1
- package/dist/checks/robots-txt.d.ts +0 -6
- package/dist/checks/robots-txt.d.ts.map +0 -1
- package/dist/checks/robots-txt.js +0 -367
- package/dist/checks/robots-txt.js.map +0 -1
- package/dist/checks/rsl.d.ts +0 -4
- package/dist/checks/rsl.d.ts.map +0 -1
- package/dist/checks/rsl.js +0 -242
- package/dist/checks/rsl.js.map +0 -1
- package/dist/checks/security-txt.d.ts +0 -4
- package/dist/checks/security-txt.d.ts.map +0 -1
- package/dist/checks/security-txt.js +0 -81
- package/dist/checks/security-txt.js.map +0 -1
- package/dist/checks/seo-basics.d.ts +0 -13
- package/dist/checks/seo-basics.d.ts.map +0 -1
- package/dist/checks/seo-basics.js +0 -221
- package/dist/checks/seo-basics.js.map +0 -1
- package/dist/checks/sitemap.d.ts +0 -12
- package/dist/checks/sitemap.d.ts.map +0 -1
- package/dist/checks/sitemap.js +0 -240
- package/dist/checks/sitemap.js.map +0 -1
- package/dist/checks/structured-data.d.ts +0 -4
- package/dist/checks/structured-data.d.ts.map +0 -1
- package/dist/checks/structured-data.js +0 -408
- package/dist/checks/structured-data.js.map +0 -1
- package/dist/checks/structured-fields.d.ts +0 -46
- package/dist/checks/structured-fields.d.ts.map +0 -1
- package/dist/checks/structured-fields.js +0 -112
- package/dist/checks/structured-fields.js.map +0 -1
- package/dist/checks/surface.d.ts +0 -59
- package/dist/checks/surface.d.ts.map +0 -1
- package/dist/checks/surface.js +0 -106
- package/dist/checks/surface.js.map +0 -1
- package/dist/checks/tls-https.d.ts +0 -13
- package/dist/checks/tls-https.d.ts.map +0 -1
- package/dist/checks/tls-https.js +0 -163
- package/dist/checks/tls-https.js.map +0 -1
- package/dist/checks/usage-policy.d.ts +0 -53
- package/dist/checks/usage-policy.d.ts.map +0 -1
- package/dist/checks/usage-policy.js +0 -339
- package/dist/checks/usage-policy.js.map +0 -1
- package/dist/checks/utils.d.ts +0 -40
- package/dist/checks/utils.d.ts.map +0 -1
- package/dist/checks/utils.js +0 -74
- package/dist/checks/utils.js.map +0 -1
- package/dist/checks/waf.d.ts +0 -75
- package/dist/checks/waf.d.ts.map +0 -1
- package/dist/checks/waf.js +0 -203
- package/dist/checks/waf.js.map +0 -1
- package/dist/checks/webmcp.d.ts +0 -55
- package/dist/checks/webmcp.d.ts.map +0 -1
- package/dist/checks/webmcp.js +0 -209
- package/dist/checks/webmcp.js.map +0 -1
- package/dist/checks/well-known.d.ts +0 -38
- package/dist/checks/well-known.d.ts.map +0 -1
- package/dist/checks/well-known.js +0 -202
- package/dist/checks/well-known.js.map +0 -1
- package/dist/fetcher.d.ts +0 -15
- package/dist/fetcher.d.ts.map +0 -1
- package/dist/fetcher.js +0 -127
- package/dist/fetcher.js.map +0 -1
- package/dist/guide-urls.d.ts +0 -2
- package/dist/guide-urls.d.ts.map +0 -1
- package/dist/guide-urls.js +0 -5
- package/dist/guide-urls.js.map +0 -1
- package/docs/roadmap.md +0 -367
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"structured-data.d.ts","sourceRoot":"","sources":["../../src/checks/structured-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAW,MAAM,aAAa,CAAC;AAGjF,eAAO,MAAM,IAAI,EAAE,SAIlB,CAAC;AAEF,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA4H3E"}
|
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import { guideUrl } from '../guide-urls.js';
|
|
2
|
-
import { extractVisibleText, findJsonLdBlocks } from './html-utils.js';
|
|
3
|
-
import { buildResult } from './utils.js';
|
|
4
|
-
export const meta = {
|
|
5
|
-
id: 'structured-data',
|
|
6
|
-
name: 'Structured Data',
|
|
7
|
-
description: 'Checks JSON-LD structured data on homepage',
|
|
8
|
-
};
|
|
9
|
-
export default async function check(ctx) {
|
|
10
|
-
const start = performance.now();
|
|
11
|
-
const findings = [];
|
|
12
|
-
let score = 100;
|
|
13
|
-
const html = ctx.html;
|
|
14
|
-
if (!html) {
|
|
15
|
-
findings.push({ status: 'fail', message: 'Could not fetch homepage HTML' });
|
|
16
|
-
return buildResult(meta, 0, findings, start);
|
|
17
|
-
}
|
|
18
|
-
const blocks = findJsonLdBlocks(html);
|
|
19
|
-
if (blocks.length === 0) {
|
|
20
|
-
findings.push({
|
|
21
|
-
status: 'fail',
|
|
22
|
-
message: 'No JSON-LD structured data found',
|
|
23
|
-
hint: 'Add a <script type="application/ld+json"> block in your HTML <head> with schema.org structured data describing your site, organization, or person.',
|
|
24
|
-
learnMoreUrl: guideUrl(meta.id, 'not-found'),
|
|
25
|
-
});
|
|
26
|
-
return buildResult(meta, 0, findings, start);
|
|
27
|
-
}
|
|
28
|
-
findings.push({ status: 'pass', message: `${blocks.length} JSON-LD block(s) found` });
|
|
29
|
-
const parsed = [];
|
|
30
|
-
for (const block of blocks) {
|
|
31
|
-
const raw = unescapeHtml(block);
|
|
32
|
-
try {
|
|
33
|
-
parsed.push(JSON.parse(raw));
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
findings.push({
|
|
37
|
-
status: 'warn',
|
|
38
|
-
message: 'Invalid JSON in a JSON-LD block',
|
|
39
|
-
hint: 'Validate your JSON-LD syntax. Check for trailing commas, missing quotes, or unescaped characters. Use https://validator.schema.org/ to test.',
|
|
40
|
-
learnMoreUrl: guideUrl(meta.id, 'invalid-json'),
|
|
41
|
-
});
|
|
42
|
-
score -= 10;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (parsed.length === 0) {
|
|
46
|
-
findings.push({
|
|
47
|
-
status: 'fail',
|
|
48
|
-
message: 'All JSON-LD blocks have invalid JSON',
|
|
49
|
-
hint: 'Fix the JSON syntax errors in your JSON-LD blocks. Use a JSON linter or https://validator.schema.org/ to validate.',
|
|
50
|
-
learnMoreUrl: guideUrl(meta.id, 'all-invalid'),
|
|
51
|
-
});
|
|
52
|
-
return buildResult(meta, 10, findings, start);
|
|
53
|
-
}
|
|
54
|
-
const hasContext = parsed.some((d) => isSchemaOrgContext(d['@context']));
|
|
55
|
-
if (hasContext) {
|
|
56
|
-
findings.push({ status: 'pass', message: '@context references schema.org' });
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
findings.push({
|
|
60
|
-
status: 'warn',
|
|
61
|
-
message: 'No @context referencing schema.org',
|
|
62
|
-
hint: 'Add "@context": "https://schema.org" to your JSON-LD root object.',
|
|
63
|
-
learnMoreUrl: guideUrl(meta.id, 'missing-context'),
|
|
64
|
-
});
|
|
65
|
-
score -= 15;
|
|
66
|
-
}
|
|
67
|
-
const hasGraph = parsed.some((d) => Array.isArray(d['@graph']));
|
|
68
|
-
if (hasGraph) {
|
|
69
|
-
findings.push({ status: 'pass', message: '@graph array present (multi-entity structured data)' });
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
findings.push({
|
|
73
|
-
status: 'warn',
|
|
74
|
-
message: 'No @graph array (single-entity only)',
|
|
75
|
-
hint: 'Use an @graph array to define multiple entities in one JSON-LD block: { "@context": "https://schema.org", "@graph": [...] }',
|
|
76
|
-
learnMoreUrl: guideUrl(meta.id, 'no-graph'),
|
|
77
|
-
});
|
|
78
|
-
score -= 5;
|
|
79
|
-
}
|
|
80
|
-
const allTypes = new Set();
|
|
81
|
-
for (const d of parsed) {
|
|
82
|
-
collectTypes(d, allTypes);
|
|
83
|
-
}
|
|
84
|
-
const importantTypes = ['Person', 'Organization', 'WebSite', 'WebPage', 'ProfilePage'];
|
|
85
|
-
const foundTypes = importantTypes.filter((t) => allTypes.has(t));
|
|
86
|
-
if (foundTypes.length >= 2) {
|
|
87
|
-
findings.push({ status: 'pass', message: `Key types found: ${foundTypes.join(', ')}` });
|
|
88
|
-
}
|
|
89
|
-
else if (foundTypes.length === 1) {
|
|
90
|
-
findings.push({
|
|
91
|
-
status: 'warn',
|
|
92
|
-
message: `Only 1 key type found: ${foundTypes[0]}`,
|
|
93
|
-
detail: `Consider adding: ${importantTypes.filter((t) => !allTypes.has(t)).join(', ')}`,
|
|
94
|
-
hint: 'Add more entity types to your @graph. AI agents use these to understand site structure. Common types: Person, Organization, WebSite, WebPage.',
|
|
95
|
-
learnMoreUrl: guideUrl(meta.id, 'few-types'),
|
|
96
|
-
});
|
|
97
|
-
score -= 10;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
findings.push({
|
|
101
|
-
status: 'warn',
|
|
102
|
-
message: 'No key entity types (Person, Organization, WebSite, etc.)',
|
|
103
|
-
hint: 'Add @type to your JSON-LD entities. Use Person or Organization for the owner, WebSite for the site, and WebPage for individual pages.',
|
|
104
|
-
learnMoreUrl: guideUrl(meta.id, 'no-types'),
|
|
105
|
-
});
|
|
106
|
-
score -= 15;
|
|
107
|
-
}
|
|
108
|
-
if (allTypes.has('BreadcrumbList')) {
|
|
109
|
-
findings.push({ status: 'pass', message: 'BreadcrumbList present' });
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
findings.push({
|
|
113
|
-
status: 'warn',
|
|
114
|
-
message: 'No BreadcrumbList found',
|
|
115
|
-
hint: 'Add a BreadcrumbList entity to help AI agents understand your site navigation hierarchy.',
|
|
116
|
-
learnMoreUrl: guideUrl(meta.id, 'no-breadcrumb'),
|
|
117
|
-
});
|
|
118
|
-
score -= 5;
|
|
119
|
-
}
|
|
120
|
-
// Informational in 3.x: new findings inside a weighted check must not deduct.
|
|
121
|
-
reportProvenance(parsed, findings);
|
|
122
|
-
reportFreshness(parsed, findings);
|
|
123
|
-
reportVisibleTextAgreement(parsed, html, findings);
|
|
124
|
-
return buildResult(meta, score, findings, start);
|
|
125
|
-
}
|
|
126
|
-
/* ── Provenance and freshness (informational in 3.x) ────────────────────── */
|
|
127
|
-
/** Walk a JSON-LD tree, yielding every object node. */
|
|
128
|
-
function* nodes(value, depth = 0) {
|
|
129
|
-
if (depth > 8 || value === null || typeof value !== 'object')
|
|
130
|
-
return;
|
|
131
|
-
if (Array.isArray(value)) {
|
|
132
|
-
for (const item of value)
|
|
133
|
-
yield* nodes(item, depth + 1);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const obj = value;
|
|
137
|
-
yield obj;
|
|
138
|
-
for (const child of Object.values(obj))
|
|
139
|
-
yield* nodes(child, depth + 1);
|
|
140
|
-
}
|
|
141
|
-
/** Read a property that may be a string, an object with a name, or a list of either. */
|
|
142
|
-
function readNames(value) {
|
|
143
|
-
if (typeof value === 'string')
|
|
144
|
-
return [value];
|
|
145
|
-
if (Array.isArray(value))
|
|
146
|
-
return value.flatMap(readNames);
|
|
147
|
-
if (value !== null && typeof value === 'object') {
|
|
148
|
-
const name = value.name;
|
|
149
|
-
return typeof name === 'string' ? [name] : [];
|
|
150
|
-
}
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Who wrote this, and can it be tied to an entity that exists elsewhere?
|
|
155
|
-
*
|
|
156
|
-
* An assistant deciding whether to cite a page weighs where it came from. A
|
|
157
|
-
* byline with no `sameAs` is a string; a byline linked to Wikidata, an ORCID or
|
|
158
|
-
* a company page is an entity a model can already have an opinion about.
|
|
159
|
-
*/
|
|
160
|
-
function reportProvenance(parsed, findings) {
|
|
161
|
-
const authors = new Set();
|
|
162
|
-
const sameAs = new Set();
|
|
163
|
-
let publisher = false;
|
|
164
|
-
let organizationDetail = false;
|
|
165
|
-
// Index every node that carries an @id, so a reference-shaped property
|
|
166
|
-
// reads as the node it points at. `author: {"@id": "...#organization"}`
|
|
167
|
-
// is the canonical @graph idiom — it is what Yoast emits for millions
|
|
168
|
-
// of sites — and treating it as an empty object told every one of them
|
|
169
|
-
// they had no author.
|
|
170
|
-
const byId = new Map();
|
|
171
|
-
for (const root of parsed) {
|
|
172
|
-
for (const node of nodes(root)) {
|
|
173
|
-
const id = node['@id'];
|
|
174
|
-
if (typeof id === 'string' && !byId.has(id))
|
|
175
|
-
byId.set(id, node);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const deref = (value) => {
|
|
179
|
-
if (Array.isArray(value))
|
|
180
|
-
return value.map(deref);
|
|
181
|
-
if (value !== null && typeof value === 'object') {
|
|
182
|
-
const obj = value;
|
|
183
|
-
const id = obj['@id'];
|
|
184
|
-
// Only a pure reference is followed; an inline node with its own
|
|
185
|
-
// fields already says what it has to say.
|
|
186
|
-
if (typeof id === 'string' && Object.keys(obj).length === 1)
|
|
187
|
-
return byId.get(id) ?? value;
|
|
188
|
-
}
|
|
189
|
-
return value;
|
|
190
|
-
};
|
|
191
|
-
for (const root of parsed) {
|
|
192
|
-
for (const node of nodes(root)) {
|
|
193
|
-
for (const name of readNames(deref(node.author)))
|
|
194
|
-
authors.add(name);
|
|
195
|
-
if (node.publisher !== undefined)
|
|
196
|
-
publisher = true;
|
|
197
|
-
for (const url of readNames(node.sameAs))
|
|
198
|
-
sameAs.add(url);
|
|
199
|
-
if (Array.isArray(node.sameAs)) {
|
|
200
|
-
for (const url of node.sameAs)
|
|
201
|
-
if (typeof url === 'string')
|
|
202
|
-
sameAs.add(url);
|
|
203
|
-
}
|
|
204
|
-
else if (typeof node.sameAs === 'string') {
|
|
205
|
-
sameAs.add(node.sameAs);
|
|
206
|
-
}
|
|
207
|
-
if (node.companyRegistration !== undefined ||
|
|
208
|
-
node.legalAddress !== undefined ||
|
|
209
|
-
node.contactPoint !== undefined) {
|
|
210
|
-
organizationDetail = true;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (authors.size > 0) {
|
|
215
|
-
findings.push({ status: 'pass', message: `Authorship declared: ${[...authors].slice(0, 3).join(', ')}` });
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
findings.push({
|
|
219
|
-
status: 'warn',
|
|
220
|
-
message: 'No author declared in structured data',
|
|
221
|
-
hint: 'An assistant deciding whether to cite a page weighs where it came from. Add author as a Person or ' +
|
|
222
|
-
'Organization, not a bare string.',
|
|
223
|
-
learnMoreUrl: guideUrl(meta.id, 'no-author'),
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
if (sameAs.size > 0) {
|
|
227
|
-
findings.push({
|
|
228
|
-
status: 'pass',
|
|
229
|
-
message: `${sameAs.size} sameAs link(s) tie your entity to external identifiers`,
|
|
230
|
-
detail: [...sameAs].slice(0, 4).join(', '),
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
findings.push({
|
|
235
|
-
status: 'warn',
|
|
236
|
-
message: 'No sameAs links',
|
|
237
|
-
hint: 'sameAs is what turns a name into an entity. Link your Organization or Person to Wikidata, LinkedIn, GitHub ' +
|
|
238
|
-
'or Crunchbase so a model can connect this page to what it already knows.',
|
|
239
|
-
learnMoreUrl: guideUrl(meta.id, 'no-same-as'),
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
if (!publisher && authors.size > 0) {
|
|
243
|
-
findings.push({
|
|
244
|
-
status: 'warn',
|
|
245
|
-
message: 'Author declared but no publisher',
|
|
246
|
-
hint: 'Declare publisher as well, so a citation can name the outlet as well as the writer.',
|
|
247
|
-
learnMoreUrl: guideUrl(meta.id, 'no-publisher'),
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (organizationDetail) {
|
|
251
|
-
findings.push({
|
|
252
|
-
status: 'pass',
|
|
253
|
-
message: 'Organization carries disambiguating detail (registration, legal address or contact point)',
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
/** Freshness buckets, in days. */
|
|
258
|
-
const FRESH_DAYS = 90;
|
|
259
|
-
const STALE_DAYS = 730;
|
|
260
|
-
/**
|
|
261
|
-
* When did this page last change?
|
|
262
|
-
*
|
|
263
|
-
* Assistants answering time-sensitive questions weigh recency, and a page with
|
|
264
|
-
* no date at all cannot be weighed. The check reports rather than penalises:
|
|
265
|
-
* an evergreen reference page is legitimately old.
|
|
266
|
-
*/
|
|
267
|
-
function reportFreshness(parsed, findings) {
|
|
268
|
-
const dates = [];
|
|
269
|
-
for (const root of parsed) {
|
|
270
|
-
for (const node of nodes(root)) {
|
|
271
|
-
for (const field of ['dateModified', 'datePublished', 'uploadDate']) {
|
|
272
|
-
const raw = node[field];
|
|
273
|
-
if (typeof raw !== 'string')
|
|
274
|
-
continue;
|
|
275
|
-
const parsedDate = new Date(raw);
|
|
276
|
-
if (Number.isNaN(parsedDate.getTime())) {
|
|
277
|
-
findings.push({
|
|
278
|
-
status: 'warn',
|
|
279
|
-
message: `${field} is not a parseable date: "${raw}"`,
|
|
280
|
-
hint: 'Use ISO 8601, for example 2026-09-04 or 2026-09-04T12:00:00Z.',
|
|
281
|
-
learnMoreUrl: guideUrl(meta.id, 'invalid-date'),
|
|
282
|
-
});
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
dates.push({ field, value: parsedDate });
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
if (dates.length === 0) {
|
|
290
|
-
findings.push({
|
|
291
|
-
status: 'warn',
|
|
292
|
-
message: 'No dateModified or datePublished in structured data',
|
|
293
|
-
hint: 'Assistants weigh recency when they answer time-sensitive questions, and a page with no date cannot be ' +
|
|
294
|
-
'weighed at all. Add dateModified to anything that changes.',
|
|
295
|
-
learnMoreUrl: guideUrl(meta.id, 'no-dates'),
|
|
296
|
-
});
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
const newest = dates.reduce((a, b) => (a.value > b.value ? a : b));
|
|
300
|
-
const days = Math.round((Date.now() - newest.value.getTime()) / 86_400_000);
|
|
301
|
-
if (days < 0) {
|
|
302
|
-
findings.push({
|
|
303
|
-
status: 'warn',
|
|
304
|
-
message: `${newest.field} is in the future (${newest.value.toISOString().slice(0, 10)})`,
|
|
305
|
-
hint: 'A future date reads as a mistake or as manipulation. Use the real modification time.',
|
|
306
|
-
learnMoreUrl: guideUrl(meta.id, 'future-date'),
|
|
307
|
-
});
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
findings.push({
|
|
311
|
-
status: days > STALE_DAYS ? 'warn' : 'pass',
|
|
312
|
-
message: `Content last dated ${newest.value.toISOString().slice(0, 10)} (${days} days ago)`,
|
|
313
|
-
...(days > STALE_DAYS
|
|
314
|
-
? {
|
|
315
|
-
hint: 'Over two years without a recorded update. If the page is still accurate, refresh dateModified so an assistant knows that; if it is not, it is answering with stale facts.',
|
|
316
|
-
learnMoreUrl: guideUrl(meta.id, 'stale-content'),
|
|
317
|
-
}
|
|
318
|
-
: days < FRESH_DAYS
|
|
319
|
-
? {}
|
|
320
|
-
: { detail: 'Fine for reference material; worth refreshing on anything time-sensitive.' }),
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Google's one explicit ask about structured data and AI features is that it
|
|
325
|
-
* match the visible text. Markup describing a page that is not there is the
|
|
326
|
-
* oldest form of spam, and it is now graded by systems that quote it.
|
|
327
|
-
*/
|
|
328
|
-
function reportVisibleTextAgreement(parsed, html, findings) {
|
|
329
|
-
const visible = extractVisibleText(html).toLowerCase();
|
|
330
|
-
if (visible.length < 200)
|
|
331
|
-
return;
|
|
332
|
-
const claims = [];
|
|
333
|
-
for (const root of parsed) {
|
|
334
|
-
for (const node of nodes(root)) {
|
|
335
|
-
for (const field of ['headline', 'name']) {
|
|
336
|
-
const value = node[field];
|
|
337
|
-
if (typeof value === 'string' && value.length >= 12 && value.length <= 110) {
|
|
338
|
-
claims.push({ field, value });
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (claims.length === 0)
|
|
344
|
-
return;
|
|
345
|
-
const missing = claims.filter((c) => !visible.includes(c.value.toLowerCase())).slice(0, 3);
|
|
346
|
-
if (missing.length === 0) {
|
|
347
|
-
findings.push({ status: 'pass', message: 'Structured-data headings appear in the visible text' });
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
findings.push({
|
|
351
|
-
status: 'warn',
|
|
352
|
-
message: `${missing.length} structured-data value(s) do not appear in the visible text`,
|
|
353
|
-
detail: missing.map((c) => `${c.field}: "${c.value}"`).join('\n'),
|
|
354
|
-
hint: 'Google\u2019s one explicit requirement for structured data and AI features is that it match what a reader sees. ' +
|
|
355
|
-
'Markup describing a page that is not there is the oldest form of spam, and it is now graded by systems that ' +
|
|
356
|
-
'quote it. Note this compares the static HTML, so text rendered by script will read as missing here too.',
|
|
357
|
-
learnMoreUrl: guideUrl(meta.id, 'markup-mismatch'),
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
function unescapeHtml(str) {
|
|
361
|
-
return str
|
|
362
|
-
.replace(/&/g, '&')
|
|
363
|
-
.replace(/</g, '<')
|
|
364
|
-
.replace(/>/g, '>')
|
|
365
|
-
.replace(/"/g, '"')
|
|
366
|
-
.replace(/'/g, "'")
|
|
367
|
-
.replace(/'/g, "'")
|
|
368
|
-
.replace(///g, '/');
|
|
369
|
-
}
|
|
370
|
-
function isSchemaOrgContext(context) {
|
|
371
|
-
if (typeof context === 'string') {
|
|
372
|
-
return /^https?:\/\/schema\.org\/?$/.test(context);
|
|
373
|
-
}
|
|
374
|
-
if (Array.isArray(context)) {
|
|
375
|
-
return context.some((item) => isSchemaOrgContext(item));
|
|
376
|
-
}
|
|
377
|
-
if (context && typeof context === 'object') {
|
|
378
|
-
const record = context;
|
|
379
|
-
if (typeof record['@vocab'] === 'string') {
|
|
380
|
-
return /^https?:\/\/schema\.org\/?$/.test(record['@vocab']);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
function collectTypes(obj, types, depth = 0) {
|
|
386
|
-
if (!obj || typeof obj !== 'object' || depth > 10)
|
|
387
|
-
return;
|
|
388
|
-
if (Array.isArray(obj)) {
|
|
389
|
-
obj.forEach((item) => collectTypes(item, types, depth + 1));
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
const record = obj;
|
|
393
|
-
if (record['@type']) {
|
|
394
|
-
const t = Array.isArray(record['@type']) ? record['@type'] : [record['@type']];
|
|
395
|
-
t.forEach((type) => types.add(type));
|
|
396
|
-
}
|
|
397
|
-
if (Array.isArray(record['@graph'])) {
|
|
398
|
-
record['@graph'].forEach((item) => collectTypes(item, types, depth + 1));
|
|
399
|
-
}
|
|
400
|
-
for (const [key, value] of Object.entries(record)) {
|
|
401
|
-
if (key.startsWith('@'))
|
|
402
|
-
continue;
|
|
403
|
-
if (value && typeof value === 'object') {
|
|
404
|
-
collectTypes(value, types, depth + 1);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
//# sourceMappingURL=structured-data.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"structured-data.js","sourceRoot":"","sources":["../../src/checks/structured-data.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal RFC 9651 (Structured Field Values) Dictionary parser.
|
|
3
|
-
*
|
|
4
|
-
* Scope is deliberately narrow: the fields ax-audit reads that are defined as
|
|
5
|
-
* Dictionaries carry token or boolean members with optional parameters —
|
|
6
|
-
* IETF AIPREF `Content-Usage: train-ai=n, search=y`
|
|
7
|
-
* (draft-ietf-aipref-attach-05 §2) and, on the request side, Web Bot Auth's
|
|
8
|
-
* `Signature-Input`. A full Structured Fields implementation (inner lists,
|
|
9
|
-
* byte sequences, dates, display strings) would be dead weight, so this parser
|
|
10
|
-
* covers dictionaries of bare items and reports anything else as malformed
|
|
11
|
-
* rather than guessing.
|
|
12
|
-
*
|
|
13
|
-
* The parser never throws. Malformed members are collected so callers can
|
|
14
|
-
* report *which* segment was wrong instead of discarding the whole header.
|
|
15
|
-
*/
|
|
16
|
-
export interface DictMember {
|
|
17
|
-
/** Member key, lowercased (RFC 9651 keys are case-sensitive but every field we read is lowercase). */
|
|
18
|
-
key: string;
|
|
19
|
-
/**
|
|
20
|
-
* Bare item value as written. A key with no `=` is a boolean true and
|
|
21
|
-
* yields `'?1'`, matching the RFC's serialization of a true Boolean.
|
|
22
|
-
*/
|
|
23
|
-
value: string;
|
|
24
|
-
/** Parameters attached to the member (`;key=value`), values as written. */
|
|
25
|
-
params: Record<string, string>;
|
|
26
|
-
/** The member as it appeared in the input, trimmed. */
|
|
27
|
-
raw: string;
|
|
28
|
-
}
|
|
29
|
-
export interface ParsedDictionary {
|
|
30
|
-
members: DictMember[];
|
|
31
|
-
/** Segments that could not be parsed as `key[=value][;param...]`. */
|
|
32
|
-
malformed: string[];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Parse a Structured Field Dictionary. Returns members in source order plus the
|
|
36
|
-
* segments that failed to parse.
|
|
37
|
-
*
|
|
38
|
-
* ```
|
|
39
|
-
* parseDictionary('train-ai=n, search=y')
|
|
40
|
-
* // members: [{key:'train-ai', value:'n'}, {key:'search', value:'y'}]
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export declare function parseDictionary(input: string): ParsedDictionary;
|
|
44
|
-
/** Convenience: look up one member's value, or `null` when absent. */
|
|
45
|
-
export declare function dictValue(parsed: ParsedDictionary, key: string): string | null;
|
|
46
|
-
//# sourceMappingURL=structured-fields.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"structured-fields.d.ts","sourceRoot":"","sources":["../../src/checks/structured-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,UAAU;IACzB,sGAAsG;IACtG,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,qEAAqE;IACrE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAkDD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA0C/D;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE9E"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal RFC 9651 (Structured Field Values) Dictionary parser.
|
|
3
|
-
*
|
|
4
|
-
* Scope is deliberately narrow: the fields ax-audit reads that are defined as
|
|
5
|
-
* Dictionaries carry token or boolean members with optional parameters —
|
|
6
|
-
* IETF AIPREF `Content-Usage: train-ai=n, search=y`
|
|
7
|
-
* (draft-ietf-aipref-attach-05 §2) and, on the request side, Web Bot Auth's
|
|
8
|
-
* `Signature-Input`. A full Structured Fields implementation (inner lists,
|
|
9
|
-
* byte sequences, dates, display strings) would be dead weight, so this parser
|
|
10
|
-
* covers dictionaries of bare items and reports anything else as malformed
|
|
11
|
-
* rather than guessing.
|
|
12
|
-
*
|
|
13
|
-
* The parser never throws. Malformed members are collected so callers can
|
|
14
|
-
* report *which* segment was wrong instead of discarding the whole header.
|
|
15
|
-
*/
|
|
16
|
-
/** RFC 9651 keys: lowercase alpha / `*` start, then alphanumerics and `_-.*`. */
|
|
17
|
-
const KEY_RE = /^[a-z*][a-z0-9_\-.*]*$/;
|
|
18
|
-
/**
|
|
19
|
-
* Split on top-level commas, keeping quoted strings intact so a value like
|
|
20
|
-
* `note="a, b"` stays one member.
|
|
21
|
-
*/
|
|
22
|
-
function splitTopLevel(input, separator) {
|
|
23
|
-
const parts = [];
|
|
24
|
-
let current = '';
|
|
25
|
-
let inQuote = false;
|
|
26
|
-
let escaped = false;
|
|
27
|
-
for (const ch of input) {
|
|
28
|
-
if (escaped) {
|
|
29
|
-
current += ch;
|
|
30
|
-
escaped = false;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (ch === '\\' && inQuote) {
|
|
34
|
-
current += ch;
|
|
35
|
-
escaped = true;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (ch === '"') {
|
|
39
|
-
inQuote = !inQuote;
|
|
40
|
-
current += ch;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (ch === separator && !inQuote) {
|
|
44
|
-
parts.push(current);
|
|
45
|
-
current = '';
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
current += ch;
|
|
49
|
-
}
|
|
50
|
-
parts.push(current);
|
|
51
|
-
return parts;
|
|
52
|
-
}
|
|
53
|
-
/** Strip surrounding double quotes from a string item, unescaping `\"` and `\\`. */
|
|
54
|
-
function unquote(value) {
|
|
55
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
56
|
-
return value.slice(1, -1).replace(/\\(["\\])/g, '$1');
|
|
57
|
-
}
|
|
58
|
-
return value;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Parse a Structured Field Dictionary. Returns members in source order plus the
|
|
62
|
-
* segments that failed to parse.
|
|
63
|
-
*
|
|
64
|
-
* ```
|
|
65
|
-
* parseDictionary('train-ai=n, search=y')
|
|
66
|
-
* // members: [{key:'train-ai', value:'n'}, {key:'search', value:'y'}]
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
export function parseDictionary(input) {
|
|
70
|
-
const members = [];
|
|
71
|
-
const malformed = [];
|
|
72
|
-
if (!input || !input.trim())
|
|
73
|
-
return { members, malformed };
|
|
74
|
-
for (const segment of splitTopLevel(input, ',')) {
|
|
75
|
-
const raw = segment.trim();
|
|
76
|
-
if (!raw)
|
|
77
|
-
continue;
|
|
78
|
-
const [head, ...paramParts] = splitTopLevel(raw, ';');
|
|
79
|
-
const eq = head.indexOf('=');
|
|
80
|
-
const key = (eq === -1 ? head : head.slice(0, eq)).trim().toLowerCase();
|
|
81
|
-
const rawValue = eq === -1 ? '?1' : head.slice(eq + 1).trim();
|
|
82
|
-
if (!KEY_RE.test(key) || (eq !== -1 && rawValue === '')) {
|
|
83
|
-
malformed.push(raw);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const params = {};
|
|
87
|
-
let paramsOk = true;
|
|
88
|
-
for (const part of paramParts) {
|
|
89
|
-
const p = part.trim();
|
|
90
|
-
if (!p)
|
|
91
|
-
continue;
|
|
92
|
-
const pEq = p.indexOf('=');
|
|
93
|
-
const pKey = (pEq === -1 ? p : p.slice(0, pEq)).trim().toLowerCase();
|
|
94
|
-
if (!KEY_RE.test(pKey)) {
|
|
95
|
-
paramsOk = false;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
params[pKey] = pEq === -1 ? '?1' : unquote(p.slice(pEq + 1).trim());
|
|
99
|
-
}
|
|
100
|
-
if (!paramsOk) {
|
|
101
|
-
malformed.push(raw);
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
members.push({ key, value: unquote(rawValue), params, raw });
|
|
105
|
-
}
|
|
106
|
-
return { members, malformed };
|
|
107
|
-
}
|
|
108
|
-
/** Convenience: look up one member's value, or `null` when absent. */
|
|
109
|
-
export function dictValue(parsed, key) {
|
|
110
|
-
return parsed.members.find((m) => m.key === key.toLowerCase())?.value ?? null;
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=structured-fields.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"structured-fields.js","sourceRoot":"","sources":["../../src/checks/structured-fields.ts"],"names":[],"mappings":"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"}
|