@seostudio/audit-engine 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -2
- package/dist/amp-validator.generated-HCTPRNDJ.js +4 -0
- package/dist/chunk-6QPF6O7D.js +197 -0
- package/dist/chunk-7SGWGESF.js +1753 -0
- package/dist/chunk-J4ZNQFJO.js +733 -0
- package/dist/chunk-XTLRDZOD.js +10238 -0
- package/dist/en-gb.generated-SZLUKHIP.js +7 -0
- package/dist/en-us.generated-H6EP75K6.js +7 -0
- package/dist/es.generated-SYEDFESZ.js +7 -0
- package/dist/facts.d.ts +1165 -0
- package/dist/facts.js +1 -0
- package/dist/fr.generated-CQZWZDYQ.js +7 -0
- package/dist/host-gate.d.ts +68 -0
- package/dist/host-gate.js +1 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +9314 -0
- package/dist/nl.generated-MW64BR3F.js +7 -0
- package/dist/reading.d.ts +517 -0
- package/dist/reading.js +3 -0
- package/dist/resource-check.d.ts +131 -0
- package/dist/resource-check.js +3 -0
- package/dist/types.d.ts +370 -0
- package/dist/types.js +1 -0
- package/package.json +69 -5
package/dist/facts.d.ts
ADDED
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CRAWL FACTS: what one reading of a page, a site's robots.txt, its sitemaps and its resources
|
|
3
|
+
* leaves for the audit, as plain JSON shapes. The crawler writes them, the rules read them, and a host
|
|
4
|
+
* may store them as they are (SeoStudio keeps them in its database's JSON columns).
|
|
5
|
+
*
|
|
6
|
+
* Types only: this file loads nothing at runtime.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* How a page was read: `browser` - rendered by a headless browser as Google renders it, every field
|
|
10
|
+
* known (every page since full rendering); `plain` - its raw HTML alone, when the render failed;
|
|
11
|
+
* `rendered` - the retired text-only fallback (older snapshots: main text, status and headers only).
|
|
12
|
+
*/
|
|
13
|
+
type SeoPageFetchModeValue = "plain" | "rendered" | "browser";
|
|
14
|
+
/** How the browser render of a page ended: settled, cut at its time limit, or failed. */
|
|
15
|
+
type SeoPageRenderOutcomeValue = "ok" | "timeout" | "failed";
|
|
16
|
+
/** One request a page's render needed and could not complete, as the audit keeps it. */
|
|
17
|
+
interface SeoPageRenderResourceFailure {
|
|
18
|
+
/** The URL asked for. */
|
|
19
|
+
url: string;
|
|
20
|
+
/** What the page asked for. */
|
|
21
|
+
type: "script" | "stylesheet" | "xhr" | "fetch" | "other";
|
|
22
|
+
/** Why: robots.txt forbids the crawler, the server answered an error status, or nothing answered. */
|
|
23
|
+
reason: "robots" | "status" | "network";
|
|
24
|
+
/** The error status, when the server answered one. */
|
|
25
|
+
status: number | null;
|
|
26
|
+
}
|
|
27
|
+
/** A field JavaScript changed: what the raw HTML said, and what the rendered page says (empty when absent). */
|
|
28
|
+
interface SeoPageRenderChange {
|
|
29
|
+
raw: string;
|
|
30
|
+
rendered: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* WHAT THE BROWSER RENDER OF A PAGE FOUND, beside what its raw HTML said: the site audit judges the
|
|
34
|
+
* rendered page and compares the two (links, metadata and text JavaScript adds or changes, script
|
|
35
|
+
* errors, failed resources, timeouts). Values are cut short; lists keep a few examples and a count.
|
|
36
|
+
*/
|
|
37
|
+
interface SeoPageRenderFacts {
|
|
38
|
+
outcome: SeoPageRenderOutcomeValue;
|
|
39
|
+
/** Why a render failed (`unavailable`, `busy`, `navigation`, `crashed`, `navigated`, `bot_wall`, ...), or null. */
|
|
40
|
+
error: string | null;
|
|
41
|
+
/** Where a script sent the browser when the render ended on another document (`navigated`); absent otherwise. */
|
|
42
|
+
navigatedTo?: string;
|
|
43
|
+
/** How long the render took, in ms. */
|
|
44
|
+
renderMs: number;
|
|
45
|
+
/** Main-text words of the raw HTML and of the rendered page (null when it was not rendered). */
|
|
46
|
+
rawWords: number;
|
|
47
|
+
renderedWords: number | null;
|
|
48
|
+
/** Internal links of the raw HTML and of the rendered page. */
|
|
49
|
+
rawInternalLinks: number;
|
|
50
|
+
renderedInternalLinks: number | null;
|
|
51
|
+
/** Internal pages the rendered page links to that the raw HTML does not. */
|
|
52
|
+
jsOnlyLinks: {
|
|
53
|
+
count: number;
|
|
54
|
+
examples: string[];
|
|
55
|
+
};
|
|
56
|
+
/** The fields JavaScript added, changed or removed; a field left out is the same in both. */
|
|
57
|
+
changed: {
|
|
58
|
+
title?: SeoPageRenderChange;
|
|
59
|
+
metaDescription?: SeoPageRenderChange;
|
|
60
|
+
canonical?: SeoPageRenderChange;
|
|
61
|
+
robots?: SeoPageRenderChange;
|
|
62
|
+
/** The hreflang alternates, as `lang url` lines joined. */
|
|
63
|
+
hreflang?: SeoPageRenderChange & {
|
|
64
|
+
rawCount: number;
|
|
65
|
+
renderedCount: number;
|
|
66
|
+
};
|
|
67
|
+
/** The first H1 with text; absent on reads made before it was compared. */
|
|
68
|
+
h1?: SeoPageRenderChange;
|
|
69
|
+
/** The structured data types JavaScript added (absent from the raw HTML), in name order; absent when none. */
|
|
70
|
+
schemaTypesAdded?: string[];
|
|
71
|
+
};
|
|
72
|
+
/** Uncaught script errors and console errors the page logged while it rendered. */
|
|
73
|
+
consoleErrors: {
|
|
74
|
+
count: number;
|
|
75
|
+
examples: string[];
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Scripts, stylesheets and data requests on the site that robots.txt keeps the crawler from, with
|
|
79
|
+
* how many of them are stylesheets and scripts (absent on reads made before they were counted).
|
|
80
|
+
*/
|
|
81
|
+
blockedResources: {
|
|
82
|
+
count: number;
|
|
83
|
+
examples: string[];
|
|
84
|
+
stylesheets?: number;
|
|
85
|
+
scripts?: number;
|
|
86
|
+
};
|
|
87
|
+
/** Scripts and stylesheets that failed to load: an error status, or no answer. */
|
|
88
|
+
failedResources: {
|
|
89
|
+
count: number;
|
|
90
|
+
examples: SeoPageRenderResourceFailure[];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* How the parser read the RAW HTML's head as served, beside the stored page's own
|
|
94
|
+
* ({@link SeoPageMarkupFacts.head}); absent on reads made before it was kept.
|
|
95
|
+
*/
|
|
96
|
+
rawHead?: SeoPageHeadFacts | null;
|
|
97
|
+
/**
|
|
98
|
+
* The head elements search engines read (canonical, robots, hreflang, title, description) that the
|
|
99
|
+
* parser placed in `<body>` when it read the RAW HTML as served; absent on reads made before it was
|
|
100
|
+
* kept.
|
|
101
|
+
*/
|
|
102
|
+
rawOutsideHead?: ("canonical" | "robots" | "hreflang" | "title" | "description")[];
|
|
103
|
+
/**
|
|
104
|
+
* What the in-page probe measured on the rendered page (layout, images, overlays, history, weight);
|
|
105
|
+
* absent on reads made before it ran, null when it could not run.
|
|
106
|
+
*/
|
|
107
|
+
dom?: SeoPageDomFacts | null;
|
|
108
|
+
/** What the accessibility checks (axe-core) found; absent when they did not run on this read. */
|
|
109
|
+
a11y?: SeoPageA11yFacts | null;
|
|
110
|
+
/**
|
|
111
|
+
* The same page rendered as a desktop browser sees it (the desktop app's second render), for the
|
|
112
|
+
* mobile and desktop parity check; absent when there was no second render.
|
|
113
|
+
*/
|
|
114
|
+
desktop?: SeoPageDesktopView | null;
|
|
115
|
+
}
|
|
116
|
+
/** One image of a rendered page as the probe measured it. */
|
|
117
|
+
interface SeoPageDomImage {
|
|
118
|
+
/** The URL the browser chose for it (`currentSrc`, else `src`), absolute. */
|
|
119
|
+
url: string;
|
|
120
|
+
/** Its rendered box, in CSS pixels. */
|
|
121
|
+
width: number;
|
|
122
|
+
height: number;
|
|
123
|
+
/** How far down the page its top edge sits, in CSS pixels; absent on reads made before it was kept. */
|
|
124
|
+
top?: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* WHAT THE IN-PAGE PROBE MEASURED on a rendered page, on the phone's viewport, before any scroll:
|
|
128
|
+
* layout widths, text sizes, tap targets, images, the largest paint, layout shifts, the DOM's size, what
|
|
129
|
+
* covers the content, links built from other elements, the history entries the page added, infinite
|
|
130
|
+
* scroll, and what the page transferred. Lists keep a few examples; values are cut short.
|
|
131
|
+
*/
|
|
132
|
+
interface SeoPageDomFacts {
|
|
133
|
+
/** The layout viewport, in CSS pixels. */
|
|
134
|
+
viewport: {
|
|
135
|
+
width: number;
|
|
136
|
+
height: number;
|
|
137
|
+
};
|
|
138
|
+
/** The document's scroll width, in CSS pixels: wider than the viewport means a horizontal scroll. */
|
|
139
|
+
contentWidth: number;
|
|
140
|
+
/** Elements that reach past the viewport's right edge, as short CSS paths. */
|
|
141
|
+
overflowing: string[];
|
|
142
|
+
/** Visible text characters, and how many of them are set under 12 CSS pixels. */
|
|
143
|
+
text: {
|
|
144
|
+
chars: number;
|
|
145
|
+
smallChars: number;
|
|
146
|
+
};
|
|
147
|
+
/** Visible tap targets, and how many are under 24 by 24 CSS pixels with no room around them. */
|
|
148
|
+
tapTargets: {
|
|
149
|
+
total: number;
|
|
150
|
+
small: number;
|
|
151
|
+
examples: string[];
|
|
152
|
+
};
|
|
153
|
+
/** The visible images, rendered size and URL. */
|
|
154
|
+
images: SeoPageDomImage[];
|
|
155
|
+
/** Images below the first screen that load at once (not lazy). */
|
|
156
|
+
offscreenEager: {
|
|
157
|
+
count: number;
|
|
158
|
+
examples: string[];
|
|
159
|
+
};
|
|
160
|
+
/** The largest content element on the first screen, or null when none was found. */
|
|
161
|
+
lcp: {
|
|
162
|
+
kind: "image" | "text" | "video";
|
|
163
|
+
selector: string;
|
|
164
|
+
/** The image's URL, for an image or a video poster. */
|
|
165
|
+
url: string | null;
|
|
166
|
+
/** Whether the image is lazy-loaded (natively or by a script). */
|
|
167
|
+
lazy: boolean;
|
|
168
|
+
/** Its `fetchpriority` attribute, or null. */
|
|
169
|
+
fetchPriority: string | null;
|
|
170
|
+
} | null;
|
|
171
|
+
/**
|
|
172
|
+
* The first screen's images that fade in (loaded at opacity 0, then shown by an opacity transition
|
|
173
|
+
* or animation): Chrome never takes one as the largest paint. Absent on renders made before it.
|
|
174
|
+
*/
|
|
175
|
+
fadedIn?: string[];
|
|
176
|
+
/** Layout shifts while the page loaded: the summed score, how many, and the shifted elements. */
|
|
177
|
+
layoutShift: {
|
|
178
|
+
score: number;
|
|
179
|
+
count: number;
|
|
180
|
+
sources: string[];
|
|
181
|
+
};
|
|
182
|
+
/** Elements, the deepest nesting and the most children of one element. */
|
|
183
|
+
dom: {
|
|
184
|
+
elements: number;
|
|
185
|
+
depth: number;
|
|
186
|
+
width: number;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* The history entries the page added while it loaded, without any visitor action, and whether one
|
|
190
|
+
* of them repeats the entry before it (a trap for the Back button).
|
|
191
|
+
*/
|
|
192
|
+
history: {
|
|
193
|
+
pushed: number;
|
|
194
|
+
repeated: boolean;
|
|
195
|
+
};
|
|
196
|
+
/** An element fixed over the first screen covering most of the content, or null. */
|
|
197
|
+
overlay: {
|
|
198
|
+
selector: string;
|
|
199
|
+
coverage: number;
|
|
200
|
+
} | null;
|
|
201
|
+
/** Elements other than links that act as links (a link role, or a URL in a data attribute). */
|
|
202
|
+
fakeLinks: {
|
|
203
|
+
count: number;
|
|
204
|
+
examples: string[];
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Whether scrolling to the bottom loaded more content, and whether the page also offers the next
|
|
208
|
+
* part at a URL of its own (a link to a next page, or the address changing); null when not tested.
|
|
209
|
+
*/
|
|
210
|
+
infiniteScroll: {
|
|
211
|
+
grew: boolean;
|
|
212
|
+
paginated: boolean;
|
|
213
|
+
} | null;
|
|
214
|
+
/**
|
|
215
|
+
* What the page transferred while it loaded: the document, its scripts, styles and data (bytes as
|
|
216
|
+
* served), and the images and fonts it asked for (their size is the resource checks').
|
|
217
|
+
*/
|
|
218
|
+
transfer: {
|
|
219
|
+
bytes: number;
|
|
220
|
+
requests: number;
|
|
221
|
+
images: string[];
|
|
222
|
+
fonts: string[];
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/** What the accessibility checks (axe-core) found on a rendered page. */
|
|
226
|
+
interface SeoPageA11yFacts {
|
|
227
|
+
/** Which rules ran: the search-relevant subset (the server) or all of them (the desktop app). */
|
|
228
|
+
rules: "seo" | "all";
|
|
229
|
+
/** Each rule the page fails, the elements that fail it and a few of them as CSS paths. */
|
|
230
|
+
violations: {
|
|
231
|
+
id: string;
|
|
232
|
+
count: number;
|
|
233
|
+
examples: string[];
|
|
234
|
+
}[];
|
|
235
|
+
}
|
|
236
|
+
/** The page as a desktop browser rendered it, for the mobile and desktop parity check. */
|
|
237
|
+
interface SeoPageDesktopView {
|
|
238
|
+
outcome: SeoPageRenderOutcomeValue;
|
|
239
|
+
title: string;
|
|
240
|
+
metaRobots: string;
|
|
241
|
+
canonical: string;
|
|
242
|
+
/** Main-text words. */
|
|
243
|
+
words: number;
|
|
244
|
+
schemaTypes: string[];
|
|
245
|
+
}
|
|
246
|
+
/** Where a link sits on its page. */
|
|
247
|
+
type SeoPageLinkPlacementValue = "body" | "nav" | "footer" | "image";
|
|
248
|
+
/** How serious an audit issue is. */
|
|
249
|
+
type SeoPageIssueSeverityValue = "critical" | "warning" | "notice";
|
|
250
|
+
/** One hop of a redirect chain, as a snapshot stores it. */
|
|
251
|
+
interface SeoPageRedirectHop {
|
|
252
|
+
/** The URL that answered. */
|
|
253
|
+
url: string;
|
|
254
|
+
/** Its HTTP status. */
|
|
255
|
+
status: number;
|
|
256
|
+
/**
|
|
257
|
+
* The URL exactly as the previous hop's `Location` spelled it (resolved), when it differs from `url`:
|
|
258
|
+
* an `http:` target the crawler fetched over https. Absent on the first hop and when they agree.
|
|
259
|
+
*/
|
|
260
|
+
location?: string;
|
|
261
|
+
}
|
|
262
|
+
/** One heading of a page's outline, as a snapshot stores it. */
|
|
263
|
+
interface SeoPageHeading {
|
|
264
|
+
/** 1 to 6. */
|
|
265
|
+
level: number;
|
|
266
|
+
/** The heading's text, whitespace collapsed. */
|
|
267
|
+
text: string;
|
|
268
|
+
}
|
|
269
|
+
/** One hreflang alternate, as a snapshot stores it. */
|
|
270
|
+
interface SeoPageHreflang {
|
|
271
|
+
/** The language-region code as written. */
|
|
272
|
+
lang: string;
|
|
273
|
+
/** The alternate's URL as written. */
|
|
274
|
+
url: string;
|
|
275
|
+
}
|
|
276
|
+
/** One JSON-LD block as the audit keeps it: the raw text, or a marker when the page's budget ran out. */
|
|
277
|
+
interface SeoPageJsonLdBlock {
|
|
278
|
+
/** The block's text as the page served it, or null when it was left out for size. */
|
|
279
|
+
text: string | null;
|
|
280
|
+
/** The block's size in characters. */
|
|
281
|
+
chars: number;
|
|
282
|
+
}
|
|
283
|
+
/** How an image is lazy-loaded: the native attribute, a JavaScript loader, or not at all. */
|
|
284
|
+
type SeoPageLazyMethodValue = "native" | "javascript" | "none";
|
|
285
|
+
/** A text value read from the markup, and whether the HTML parser placed it inside `<head>`. */
|
|
286
|
+
interface SeoPageMarkupText {
|
|
287
|
+
/** The value as written, whitespace collapsed and cut short. */
|
|
288
|
+
text: string;
|
|
289
|
+
inHead: boolean;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* HOW THE HTML PARSER READ A PAGE'S HEAD. The parse follows the HTML standard: an element that is not
|
|
293
|
+
* allowed in `<head>` (an `<img>`, a `<div>`, text, an `<iframe>` or `<img>` inside a `<noscript>` of
|
|
294
|
+
* the raw HTML) ends the head there, and everything after it lands in `<body>`. Read from the page as
|
|
295
|
+
* stored (the rendered DOM, serialized and parsed again: what a script put in the head that ends it)
|
|
296
|
+
* and, for the raw-against-rendered comparison, from the raw HTML as served
|
|
297
|
+
* ({@link SeoPageRenderFacts.rawHead}).
|
|
298
|
+
*/
|
|
299
|
+
interface SeoPageHeadFacts {
|
|
300
|
+
/** `<html>`, `<head>` and `<body>` start tags written in the source. */
|
|
301
|
+
htmlTags: number;
|
|
302
|
+
headTags: number;
|
|
303
|
+
bodyTags: number;
|
|
304
|
+
/** Whether an element or text came before the `<html>` tag. */
|
|
305
|
+
contentBeforeHtml: boolean;
|
|
306
|
+
/** Whether the head began before its own `<head>` tag (an element came first and opened it). */
|
|
307
|
+
headNotFirst: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* The element (or `#text`) that ended `<head>` before its `</head>` or `<body>` tag, so the elements
|
|
310
|
+
* after it were moved into `<body>`; null when the head ended where the source ends it.
|
|
311
|
+
*/
|
|
312
|
+
breaker: {
|
|
313
|
+
tag: string;
|
|
314
|
+
line: number;
|
|
315
|
+
inNoscript: boolean;
|
|
316
|
+
} | null;
|
|
317
|
+
}
|
|
318
|
+
/** A robots directive group: whom it addresses (`robots` for every crawler) and its tokens. */
|
|
319
|
+
interface SeoPageRobotsGroup {
|
|
320
|
+
/** `robots`, or the crawler named (`googlebot`, `bingbot`, ...), lowercased. */
|
|
321
|
+
agent: string;
|
|
322
|
+
/** Each directive, lowercased, `name:value` for the ones with a value (`max-snippet:0`). */
|
|
323
|
+
tokens: string[];
|
|
324
|
+
}
|
|
325
|
+
/** A viewport meta tag, its content as written and the properties it sets (null when not set). */
|
|
326
|
+
interface SeoPageViewport {
|
|
327
|
+
content: string;
|
|
328
|
+
width: string | null;
|
|
329
|
+
initialScale: string | null;
|
|
330
|
+
minimumScale: string | null;
|
|
331
|
+
maximumScale: string | null;
|
|
332
|
+
userScalable: string | null;
|
|
333
|
+
inHead: boolean;
|
|
334
|
+
}
|
|
335
|
+
/** The kinds of link href the audit flags. */
|
|
336
|
+
type SeoPageBadLinkKindValue = "empty" | "whitespace" | "whitespace_query" | "malformed" | "javascript" | "onclick" | "localhost" | "file" | "protocol";
|
|
337
|
+
/**
|
|
338
|
+
* The site builder or framework a page's markup shows it was made with (its generator tag, or the
|
|
339
|
+
* file paths and hosts only that platform serves), for the fix hints of the platforms whose fix differs.
|
|
340
|
+
*/
|
|
341
|
+
type SeoSitePlatformValue = "squarespace" | "wix" | "shopify" | "wordpress" | "ghost" | "webflow" | "nextjs" | "discourse" | "framer" | "joomla" | "prestashop" | "nuxt";
|
|
342
|
+
/** One image as the markup states it. */
|
|
343
|
+
interface SeoPageMarkupImage {
|
|
344
|
+
/** The URL the image shows (src, a lazy-loader's attribute, or the first srcset entry), as written. */
|
|
345
|
+
src: string;
|
|
346
|
+
/** The alt text cut short, or null when the attribute is absent (`""` is decorative). */
|
|
347
|
+
alt: string | null;
|
|
348
|
+
/** The alt text's full length. */
|
|
349
|
+
altLength: number;
|
|
350
|
+
/** Whether it offers sizes: a srcset (its own, a lazy-loader's or a `<picture>` source's), and `sizes`. */
|
|
351
|
+
srcset: boolean;
|
|
352
|
+
sizes: boolean;
|
|
353
|
+
/** The `loading` and `fetchpriority` attributes, lowercased, or null. */
|
|
354
|
+
loading: string | null;
|
|
355
|
+
fetchPriority: string | null;
|
|
356
|
+
/** The `width` and `height` attributes as written, or null. */
|
|
357
|
+
width: string | null;
|
|
358
|
+
height: string | null;
|
|
359
|
+
}
|
|
360
|
+
/** One microdata or RDFa item of the page's schema.org markup, read into the shape of a JSON-LD item. */
|
|
361
|
+
interface SeoPageMarkupItem {
|
|
362
|
+
syntax: "microdata" | "rdfa";
|
|
363
|
+
/** The item as JSON (`@type`, `@id` and its properties, nested items inline). */
|
|
364
|
+
json: string;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A price or a rating the page's structured data states (any syntax), and whether the page's visible
|
|
368
|
+
* text shows it: an offer's prices (`price`, `lowPrice`), or a rating's value and counts.
|
|
369
|
+
*/
|
|
370
|
+
interface SeoPageMarkedValues {
|
|
371
|
+
/** The type holding the offers or the rating (`Product`, `LocalBusiness`, `AggregateRating`). */
|
|
372
|
+
type: string;
|
|
373
|
+
property: "price" | "rating";
|
|
374
|
+
/** The values stated, as numbers with two decimals. */
|
|
375
|
+
values: string[];
|
|
376
|
+
/** Whether the visible page shows any of them. */
|
|
377
|
+
visible: boolean;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* WHAT THE PAGE'S MARKUP STATES, read by a standards-following HTML parser that keeps where each element
|
|
381
|
+
* landed: every title, description, robots meta, canonical, `<base>`, refresh and viewport (several of
|
|
382
|
+
* one is a finding), the social tags, the links the crawler cannot follow, bookmarks, forms, images,
|
|
383
|
+
* pagination, AMP, analytics and placeholder text. Read from the page as stored: the rendered DOM when
|
|
384
|
+
* the browser rendered it (every page check judges the rendered page), the raw HTML only when it did
|
|
385
|
+
* not. Facts only, cut short; the rules in `./rules` judge them.
|
|
386
|
+
*/
|
|
387
|
+
interface SeoPageMarkupFacts {
|
|
388
|
+
/** How the parser read the stored page's head. */
|
|
389
|
+
head: SeoPageHeadFacts;
|
|
390
|
+
/** Every `<title>` (svg titles left out). */
|
|
391
|
+
titles: SeoPageMarkupText[];
|
|
392
|
+
/** Every `<meta name="description">`. */
|
|
393
|
+
descriptions: SeoPageMarkupText[];
|
|
394
|
+
/** Every robots meta (`robots`, `googlebot`, `bingbot`, ...), its tokens parsed. */
|
|
395
|
+
robots: (SeoPageRobotsGroup & {
|
|
396
|
+
inHead: boolean;
|
|
397
|
+
})[];
|
|
398
|
+
/** Every `<link rel="canonical">`: the href as written, and the attributes Google ignores it for. */
|
|
399
|
+
canonicals: {
|
|
400
|
+
href: string;
|
|
401
|
+
inHead: boolean;
|
|
402
|
+
attributes: string[];
|
|
403
|
+
}[];
|
|
404
|
+
/** hreflang `<link>` elements the parser placed in `<body>` (ignored there). */
|
|
405
|
+
hreflangOutsideHead: number;
|
|
406
|
+
/** Every `<base>`: its href as written, or null when it has none. */
|
|
407
|
+
bases: {
|
|
408
|
+
href: string | null;
|
|
409
|
+
inHead: boolean;
|
|
410
|
+
}[];
|
|
411
|
+
/** Every `<meta http-equiv="refresh">`: the content, the delay in seconds and the target, resolved. */
|
|
412
|
+
refresh: {
|
|
413
|
+
content: string;
|
|
414
|
+
delaySeconds: number | null;
|
|
415
|
+
url: string | null;
|
|
416
|
+
inHead: boolean;
|
|
417
|
+
}[];
|
|
418
|
+
viewports: SeoPageViewport[];
|
|
419
|
+
/** `<html lang>` as written (null without the attribute) and `<meta http-equiv="content-language">`. */
|
|
420
|
+
lang: {
|
|
421
|
+
html: string | null;
|
|
422
|
+
contentLanguage: string | null;
|
|
423
|
+
};
|
|
424
|
+
/** The Open Graph and X (Twitter) card fields, first value of each, as written. */
|
|
425
|
+
openGraph: {
|
|
426
|
+
title: string | null;
|
|
427
|
+
type: string | null;
|
|
428
|
+
image: string | null;
|
|
429
|
+
url: string | null;
|
|
430
|
+
description: string | null;
|
|
431
|
+
siteName: string | null;
|
|
432
|
+
};
|
|
433
|
+
twitter: {
|
|
434
|
+
card: string | null;
|
|
435
|
+
title: string | null;
|
|
436
|
+
description: string | null;
|
|
437
|
+
image: string | null;
|
|
438
|
+
site: string | null;
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Links a crawler cannot follow or that leak a local address, by kind, with a few hrefs as written
|
|
442
|
+
* and, at the same index, each one's anchor text (`anchors`, absent on older reads), so a reader can
|
|
443
|
+
* tell which link it is (an empty address says nothing by itself). `inText` counts the ones in an
|
|
444
|
+
* article's running text (a paragraph, list or code block of an `<article>` or `<main>`, outside
|
|
445
|
+
* its navigation, header, footer and asides), absent when none is or on older reads.
|
|
446
|
+
*/
|
|
447
|
+
badLinks: {
|
|
448
|
+
kind: SeoPageBadLinkKindValue;
|
|
449
|
+
count: number;
|
|
450
|
+
examples: string[];
|
|
451
|
+
anchors?: string[];
|
|
452
|
+
inText?: number;
|
|
453
|
+
}[];
|
|
454
|
+
/** Bookmarks: same-page `#fragment` links whose target is not on the page, links to a fragment of another page, and this page's element ids. */
|
|
455
|
+
bookmarks: {
|
|
456
|
+
missing: string[];
|
|
457
|
+
missingCount: number;
|
|
458
|
+
/** Links to another page's fragment: the page (resolved, no fragment) and the fragment. */
|
|
459
|
+
external: {
|
|
460
|
+
url: string;
|
|
461
|
+
fragment: string;
|
|
462
|
+
}[];
|
|
463
|
+
/** The ids (and `<a name>`s) on this page, for the bookmark links other pages hold. */
|
|
464
|
+
ids: string[];
|
|
465
|
+
idsTruncated: boolean;
|
|
466
|
+
};
|
|
467
|
+
/** Every form: where it posts (resolved, or as written when it cannot be), how, and whether it asks for a password. */
|
|
468
|
+
forms: {
|
|
469
|
+
action: string;
|
|
470
|
+
method: string;
|
|
471
|
+
password: boolean;
|
|
472
|
+
}[];
|
|
473
|
+
formCount: number;
|
|
474
|
+
/** Whether a password field sits outside any form. */
|
|
475
|
+
passwordOutsideForm: boolean;
|
|
476
|
+
/** The images the page shows (fallbacks in `<noscript>` and hidden ones left out), the first of them. */
|
|
477
|
+
images: SeoPageMarkupImage[];
|
|
478
|
+
imageCount: number;
|
|
479
|
+
/** `<link rel="next">` and `<link rel="prev">` targets, resolved. */
|
|
480
|
+
pagination: {
|
|
481
|
+
next: string[];
|
|
482
|
+
prev: string[];
|
|
483
|
+
};
|
|
484
|
+
/** Whether the page is an AMP page (`<html amp>`), and its `rel="amphtml"` target. */
|
|
485
|
+
amp: {
|
|
486
|
+
isAmp: boolean;
|
|
487
|
+
amphtml: string | null;
|
|
488
|
+
};
|
|
489
|
+
/** A separate mobile page named by `<link rel="alternate" media>`. */
|
|
490
|
+
mobileAlternate: {
|
|
491
|
+
url: string;
|
|
492
|
+
media: string;
|
|
493
|
+
} | null;
|
|
494
|
+
/**
|
|
495
|
+
* The Google Analytics and Tag Manager ids the page loads, and the ones it counts twice: an id
|
|
496
|
+
* configured twice by inline code, or a Tag Manager container loaded twice. A `gtag.js` loader
|
|
497
|
+
* repeated is not counted: Tag Manager and gtag.js load their own copy of it for the same id.
|
|
498
|
+
*/
|
|
499
|
+
analytics: {
|
|
500
|
+
ids: string[];
|
|
501
|
+
duplicates: string[];
|
|
502
|
+
};
|
|
503
|
+
/** The words around a "lorem ipsum" placeholder, or null when there is none. */
|
|
504
|
+
loremIpsum: string | null;
|
|
505
|
+
/**
|
|
506
|
+
* Plugin content and image maps; `legacy` counts the objects and embeds that need a plugin no browser
|
|
507
|
+
* runs (Flash, Silverlight, Java), absent on reads made before it was kept.
|
|
508
|
+
*/
|
|
509
|
+
plugins: {
|
|
510
|
+
object: number;
|
|
511
|
+
embed: number;
|
|
512
|
+
applet: number;
|
|
513
|
+
imageMaps: number;
|
|
514
|
+
legacy?: number;
|
|
515
|
+
};
|
|
516
|
+
/** Whether the page opts into the retired AJAX crawling scheme (`<meta name="fragment" content="!">`). */
|
|
517
|
+
ajaxFragmentMeta: boolean;
|
|
518
|
+
/** Subresources loaded with a protocol-relative URL (`//host/...`). */
|
|
519
|
+
protocolRelative: {
|
|
520
|
+
count: number;
|
|
521
|
+
examples: string[];
|
|
522
|
+
};
|
|
523
|
+
/** The microdata and RDFa items (schema.org), within a size budget; absent on reads made before they were kept. */
|
|
524
|
+
structuredItems?: SeoPageMarkupItem[];
|
|
525
|
+
/** The prices and ratings the structured data states and whether the page shows them; absent on older reads. */
|
|
526
|
+
markedValues?: SeoPageMarkedValues[];
|
|
527
|
+
/** Whether a bound on the structured data read (size, nesting, work) cut it short; absent when it did not. */
|
|
528
|
+
structuredTruncated?: boolean;
|
|
529
|
+
/**
|
|
530
|
+
* Whether the parser's work bound stopped the markup read before the page's end (a crafted page that
|
|
531
|
+
* makes the parser build far more than it holds), so these facts cover only its start; absent when not.
|
|
532
|
+
*/
|
|
533
|
+
parseCut?: boolean;
|
|
534
|
+
/**
|
|
535
|
+
* The hash of the page's layout skeleton (its container elements, not its content), which the
|
|
536
|
+
* template grouping reads; absent on reads made before it was kept.
|
|
537
|
+
*/
|
|
538
|
+
skeleton?: string | null;
|
|
539
|
+
/**
|
|
540
|
+
* The page's markdown version named by `<link rel="alternate" type="text/markdown">`, resolved, or
|
|
541
|
+
* null; absent on reads made before it was kept.
|
|
542
|
+
*/
|
|
543
|
+
markdownAlternate?: string | null;
|
|
544
|
+
/** The site builder or framework the page shows it was made with, or null; absent on older reads. */
|
|
545
|
+
platform?: SeoSitePlatformValue | null;
|
|
546
|
+
/**
|
|
547
|
+
* What the page shows of a store: its controls that put a product in the cart, and whether its text
|
|
548
|
+
* shows a price; absent on older reads.
|
|
549
|
+
*/
|
|
550
|
+
commerce?: {
|
|
551
|
+
cartButtons: number;
|
|
552
|
+
price: boolean;
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
/** What the TLS handshake with the site's host showed (the server's own read). */
|
|
556
|
+
interface SeoTlsFacts {
|
|
557
|
+
/** When it was read (ISO). */
|
|
558
|
+
checkedAt: string;
|
|
559
|
+
host: string;
|
|
560
|
+
/** The certificate's validity window (ISO), null when no certificate was read. */
|
|
561
|
+
validFrom: string | null;
|
|
562
|
+
validTo: string | null;
|
|
563
|
+
/** The protocol and cipher negotiated (`TLSv1.3`, `TLS_AES_256_GCM_SHA384`). */
|
|
564
|
+
protocol: string | null;
|
|
565
|
+
cipher: string | null;
|
|
566
|
+
/** The issuer's organisation or common name. */
|
|
567
|
+
issuer: string | null;
|
|
568
|
+
/** Whether the certificate chain and host name verified, and the verification error code when not. */
|
|
569
|
+
authorized: boolean;
|
|
570
|
+
error: string | null;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* How the site's robots.txt answered at a plan: a file (`ok`), a 4xx (`client_error`: no file, which
|
|
574
|
+
* allows everything), a 5xx (`server_error`), no answer in time (`timeout`), redirects that come back
|
|
575
|
+
* to a URL already asked (`redirect_loop`), or any other failure (`failed`: refused, a network error,
|
|
576
|
+
* a redirect chain too long).
|
|
577
|
+
*/
|
|
578
|
+
type SeoRobotsFetchOutcomeValue = "ok" | "client_error" | "server_error" | "timeout" | "redirect_loop" | "failed";
|
|
579
|
+
/** The robots.txt fetch of the last plan (the server's own read). */
|
|
580
|
+
interface SeoRobotsFetchFacts {
|
|
581
|
+
/** When it was read (ISO). */
|
|
582
|
+
checkedAt: string;
|
|
583
|
+
/** The last answer's status, or null when nothing answered. */
|
|
584
|
+
status: number | null;
|
|
585
|
+
outcome: SeoRobotsFetchOutcomeValue;
|
|
586
|
+
/**
|
|
587
|
+
* Plans in a row, this one included, whose read was unreachable (`server_error`, `timeout` or
|
|
588
|
+
* `redirect_loop`); 0 when this one was not.
|
|
589
|
+
*/
|
|
590
|
+
failures: number;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* One status read of a host variant or a page over another scheme: never followed, only its status
|
|
594
|
+
* and `Location` kept.
|
|
595
|
+
*/
|
|
596
|
+
interface SeoHostProbe {
|
|
597
|
+
/** The URL asked (`http://host/`, `https://www.host/`, `http://host/path`). */
|
|
598
|
+
url: string;
|
|
599
|
+
/** The status, or null when nothing answered. */
|
|
600
|
+
status: number | null;
|
|
601
|
+
/** The `Location` header resolved against the URL, or null. */
|
|
602
|
+
location: string | null;
|
|
603
|
+
/**
|
|
604
|
+
* Why nothing answered: the host does not exist in DNS (`unresolved`), the guard refused it
|
|
605
|
+
* (`blocked`), no answer in time (`timeout`), or any other failure (`network`).
|
|
606
|
+
*/
|
|
607
|
+
error: "unresolved" | "blocked" | "timeout" | "network" | null;
|
|
608
|
+
/**
|
|
609
|
+
* The canonical a version that answered a page names (its `Link` header, or a `<link rel=canonical>`
|
|
610
|
+
* near the top of its HTML on https), resolved; null when it names none, absent on older reads.
|
|
611
|
+
*/
|
|
612
|
+
canonical?: string | null;
|
|
613
|
+
}
|
|
614
|
+
/** The host variant reads of the last plan (the server's own). */
|
|
615
|
+
interface SeoHostProbes {
|
|
616
|
+
/** When they were read (ISO). */
|
|
617
|
+
checkedAt: string;
|
|
618
|
+
/** The home page over http and https, on the site's host and its `www.` twin. */
|
|
619
|
+
variants: SeoHostProbe[];
|
|
620
|
+
/** A few crawled pages over plain http, each with its page key. */
|
|
621
|
+
pages: (SeoHostProbe & {
|
|
622
|
+
pageKey: string;
|
|
623
|
+
})[];
|
|
624
|
+
/**
|
|
625
|
+
* A few hreflang alternates the site's pages name on OTHER domains (one page per domain), read for
|
|
626
|
+
* the return-link check; absent on older reads.
|
|
627
|
+
*/
|
|
628
|
+
hreflang?: SeoHreflangProbe[];
|
|
629
|
+
}
|
|
630
|
+
/** One hreflang alternate on another domain, as the plan read it. */
|
|
631
|
+
interface SeoHreflangProbe {
|
|
632
|
+
/** The alternate's URL as a page of the site names it. */
|
|
633
|
+
url: string;
|
|
634
|
+
/** Its page key. */
|
|
635
|
+
key: string;
|
|
636
|
+
/** The status, or null when nothing answered (or that domain's robots.txt kept the crawler off). */
|
|
637
|
+
status: number | null;
|
|
638
|
+
/**
|
|
639
|
+
* The page keys the alternate's own hreflang names (its HTML head and `Link` header); null when it
|
|
640
|
+
* was not read as a page (no 2xx HTML answer, robots.txt refused it).
|
|
641
|
+
*/
|
|
642
|
+
names: string[] | null;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* WHAT THE CONTENT RULES READ FROM A PAGE'S MAIN TEXT, worked out when the page is read so the text
|
|
646
|
+
* itself is never kept for them: a fingerprint for near-duplicate pages, the languages the text reads
|
|
647
|
+
* as, and how easy it is to read. Computed from the stored page (the rendered one whenever the browser
|
|
648
|
+
* rendered it).
|
|
649
|
+
*/
|
|
650
|
+
interface SeoPageTextFacts {
|
|
651
|
+
/**
|
|
652
|
+
* The MinHash signature of the main text's word three-grams (numbers and dates folded), base64 of
|
|
653
|
+
* little-endian 32-bit values; null when the text is too short to compare.
|
|
654
|
+
*/
|
|
655
|
+
minhash: string | null;
|
|
656
|
+
/**
|
|
657
|
+
* The languages the main text plausibly reads as, the likeliest first (ISO 639-1, or 639-3 for a
|
|
658
|
+
* language without a two-letter code); null when the text is too short to tell.
|
|
659
|
+
*/
|
|
660
|
+
languages: string[] | null;
|
|
661
|
+
/** The reading ease score (Flesch, the formula of the text's language) and that language; null when no formula fits. */
|
|
662
|
+
readability: {
|
|
663
|
+
language: string;
|
|
664
|
+
score: number;
|
|
665
|
+
} | null;
|
|
666
|
+
/**
|
|
667
|
+
* True when the main text is only a shop platform's empty listing notice ("No products available
|
|
668
|
+
* yet"); absent otherwise, and on pages read before it was kept.
|
|
669
|
+
*/
|
|
670
|
+
emptyListing?: true;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* The words a spelling check of a page did not find in its language's dictionary, after the words it
|
|
674
|
+
* never judges (names, codes, addresses, words with digits) were left out. The site-wide rule drops a
|
|
675
|
+
* word that several pages share (a term of the site, not a typo).
|
|
676
|
+
*/
|
|
677
|
+
interface SeoPageSpellingFacts {
|
|
678
|
+
/** The dictionary's language (ISO 639-1). */
|
|
679
|
+
language: string;
|
|
680
|
+
/** The main text words judged. */
|
|
681
|
+
checked: number;
|
|
682
|
+
/** The unknown words of the main text (lowercased), the most frequent first, each with its count. */
|
|
683
|
+
text: {
|
|
684
|
+
word: string;
|
|
685
|
+
count: number;
|
|
686
|
+
}[];
|
|
687
|
+
/** The unknown words of the title and of the H1 (lowercased), each absent from the main text. */
|
|
688
|
+
title: string[];
|
|
689
|
+
h1: string[];
|
|
690
|
+
}
|
|
691
|
+
/** One error the AMP validator found on an AMP page. */
|
|
692
|
+
interface SeoPageAmpError {
|
|
693
|
+
/** The validator's error code (`MANDATORY_TAG_MISSING`, `DISALLOWED_TAG`, ...). */
|
|
694
|
+
code: string;
|
|
695
|
+
/** The error's parameters (the tag, the attribute), cut short. */
|
|
696
|
+
params: string[];
|
|
697
|
+
line: number;
|
|
698
|
+
}
|
|
699
|
+
/** What the AMP validator found on an AMP page's served HTML. */
|
|
700
|
+
interface SeoPageAmpFacts {
|
|
701
|
+
/** The validator's errors (warnings left out), the first of them. */
|
|
702
|
+
errors: SeoPageAmpError[];
|
|
703
|
+
errorCount: number;
|
|
704
|
+
}
|
|
705
|
+
/** What one of the project's custom search or extraction patterns found on a page. */
|
|
706
|
+
interface SeoPageCustomResult {
|
|
707
|
+
/** The pattern's id in the project's settings. */
|
|
708
|
+
id: string;
|
|
709
|
+
/** How many times it matched. */
|
|
710
|
+
count: number;
|
|
711
|
+
/** The values it extracted (an extraction pattern's), the first of them, cut short. */
|
|
712
|
+
values: string[];
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* One problem the grammar check (Harper, on a member's computer only) found in a page's English text:
|
|
716
|
+
* the lint's kind as Harper names it (`Grammar`, `Repetition`, `Agreement`, ...), the text it is about
|
|
717
|
+
* and its first suggested replacement, each cut short. No English message is kept.
|
|
718
|
+
*/
|
|
719
|
+
interface SeoPageGrammarIssue {
|
|
720
|
+
kind: string;
|
|
721
|
+
text: string;
|
|
722
|
+
suggestion: string | null;
|
|
723
|
+
}
|
|
724
|
+
/** What the grammar check found on a page's title, H1 and main text. */
|
|
725
|
+
interface SeoPageGrammarFacts {
|
|
726
|
+
/** The characters of text checked. */
|
|
727
|
+
checked: number;
|
|
728
|
+
/** How many problems it found. */
|
|
729
|
+
count: number;
|
|
730
|
+
/** The first of them. */
|
|
731
|
+
issues: SeoPageGrammarIssue[];
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* What one of the project's custom JavaScript snippets answered on a page (run on a member's computer
|
|
735
|
+
* only, in an isolated world of the rendered page): its value as text, cut short, or why there is none.
|
|
736
|
+
*/
|
|
737
|
+
interface SeoPageScriptResult {
|
|
738
|
+
/** The snippet's id in the project's settings. */
|
|
739
|
+
id: string;
|
|
740
|
+
/** The value it returned as text (JSON for an object), or null. */
|
|
741
|
+
value: string | null;
|
|
742
|
+
/** Why it answered nothing: it ran out of time or threw; null when it answered. */
|
|
743
|
+
error: "timeout" | "error" | null;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* WHAT THE OPTIONAL CHECKS FOUND ON A PAGE: spelling (on the server only for pages with search
|
|
747
|
+
* traffic, every page on a member's computer), AMP validation (AMP pages only), the project's own
|
|
748
|
+
* search and extraction patterns, and - on a member's computer only - grammar and the project's custom
|
|
749
|
+
* JavaScript snippets. Each is absent when it did not run.
|
|
750
|
+
*/
|
|
751
|
+
interface SeoPageCheckFacts {
|
|
752
|
+
spelling?: SeoPageSpellingFacts | null;
|
|
753
|
+
amp?: SeoPageAmpFacts | null;
|
|
754
|
+
custom?: SeoPageCustomResult[];
|
|
755
|
+
grammar?: SeoPageGrammarFacts | null;
|
|
756
|
+
scripts?: SeoPageScriptResult[];
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* WHAT THE SITE AUDIT READS FROM A PAGE'S LATEST FETCH, beyond the snapshot: rewritten on EVERY read
|
|
760
|
+
* (a snapshot is written only when the compared fields change, so a field the change history does not
|
|
761
|
+
* compare would go stale there). Plain data, no English: the rules (`./audit`, `./rules`)
|
|
762
|
+
* judge it.
|
|
763
|
+
*/
|
|
764
|
+
interface SeoPageAuditFacts {
|
|
765
|
+
/** When the fetch was made (ISO). */
|
|
766
|
+
readAt: string;
|
|
767
|
+
/** Every hop of the fetch, the final one included (a loop shows a URL twice). */
|
|
768
|
+
redirectChain: SeoPageRedirectHop[];
|
|
769
|
+
/**
|
|
770
|
+
* How long the page's own answer took (the last hop, its body read; redirects before it are left
|
|
771
|
+
* out on reads made since 2026-10-09), in ms, and how many bytes the body held.
|
|
772
|
+
*/
|
|
773
|
+
responseMs: number;
|
|
774
|
+
sizeBytes: number;
|
|
775
|
+
/**
|
|
776
|
+
* Where the fetch was made from, so a response time says where it was measured: the member's own
|
|
777
|
+
* computer (a desktop crawl), the platform's server in a region of the world, or the server when the
|
|
778
|
+
* deployment names no region; absent on reads made before it was kept.
|
|
779
|
+
*/
|
|
780
|
+
measuredFrom?: SeoMeasuredFromValue;
|
|
781
|
+
/** Whether a 2xx body held an HTML document its `Content-Type` did not name as HTML; absent when not. */
|
|
782
|
+
htmlUnlabelled?: boolean;
|
|
783
|
+
/**
|
|
784
|
+
* On an HTML file over what Googlebot reads (2 MB), what the cut leaves out: whether a head element
|
|
785
|
+
* search engines read (title, description, robots, canonical, hreflang) or part of the main text sits
|
|
786
|
+
* past the cut; absent on a smaller file or an older read.
|
|
787
|
+
*/
|
|
788
|
+
htmlCut?: {
|
|
789
|
+
head: boolean;
|
|
790
|
+
mainText: boolean;
|
|
791
|
+
};
|
|
792
|
+
/**
|
|
793
|
+
* The page fields the snapshot holds but does not compare (so they can go stale there), as this
|
|
794
|
+
* fetch read them; null when no HTML was read.
|
|
795
|
+
*/
|
|
796
|
+
page: {
|
|
797
|
+
h1Count: number;
|
|
798
|
+
/** H1s holding neither text nor an image with alt text; absent on reads made before it was counted. */
|
|
799
|
+
h1Empty?: number;
|
|
800
|
+
lang: string;
|
|
801
|
+
viewport: string;
|
|
802
|
+
hasOpenGraph: boolean;
|
|
803
|
+
hasTwitterCard: boolean;
|
|
804
|
+
images: number;
|
|
805
|
+
imagesMissingAlt: number;
|
|
806
|
+
/** Links to the site's own pages and to other sites the page holds, before any cap or dedupe. */
|
|
807
|
+
internalLinks: number;
|
|
808
|
+
/** Absent on reads made before it was kept. */
|
|
809
|
+
externalLinks?: number;
|
|
810
|
+
} | null;
|
|
811
|
+
/** The response headers the audit reads, null when absent. */
|
|
812
|
+
headers: {
|
|
813
|
+
strictTransportSecurity: string | null;
|
|
814
|
+
contentSecurityPolicy: string | null;
|
|
815
|
+
xContentTypeOptions: string | null;
|
|
816
|
+
xFrameOptions: string | null;
|
|
817
|
+
referrerPolicy: string | null;
|
|
818
|
+
/** The `rel="canonical"` target of a `Link` header, absolute, or null. */
|
|
819
|
+
linkCanonical: string | null;
|
|
820
|
+
/** `Content-Type`, `Content-Encoding`, `Cache-Control`, `Content-Language` and `Refresh`; absent on reads made before they were kept. */
|
|
821
|
+
contentType?: string | null;
|
|
822
|
+
contentEncoding?: string | null;
|
|
823
|
+
cacheControl?: string | null;
|
|
824
|
+
contentLanguage?: string | null;
|
|
825
|
+
refresh?: string | null;
|
|
826
|
+
/** The hreflang alternates a `Link` header names (`rel="alternate"; hreflang=...`), absolute; absent on older reads. */
|
|
827
|
+
linkHreflang?: SeoPageHreflang[];
|
|
828
|
+
};
|
|
829
|
+
/** The `X-Robots-Tag` directives per crawler; absent on reads made before they were kept. */
|
|
830
|
+
robotsHeader?: SeoPageRobotsGroup[];
|
|
831
|
+
/** What the page's markup states; absent on reads made before it was kept, null when no HTML was read. */
|
|
832
|
+
markup?: SeoPageMarkupFacts | null;
|
|
833
|
+
/** Whether the body was HTML that was read; the fields below are empty when it was not. */
|
|
834
|
+
html: boolean;
|
|
835
|
+
/** Every `<link rel="canonical">` target, absolute, in document order. */
|
|
836
|
+
canonicals: string[];
|
|
837
|
+
/** The hreflang alternates as written. */
|
|
838
|
+
hreflang: SeoPageHreflang[];
|
|
839
|
+
/** The JSON-LD blocks, in document order, within a size budget. */
|
|
840
|
+
jsonLd: SeoPageJsonLdBlock[];
|
|
841
|
+
/** `http:` subresources on the https page: scripts, frames, stylesheets (active) and media (passive). */
|
|
842
|
+
mixedContent: {
|
|
843
|
+
active: number;
|
|
844
|
+
passive: number;
|
|
845
|
+
examples: string[];
|
|
846
|
+
};
|
|
847
|
+
/** The images the audit judges (fallbacks inside `<noscript>` left out). */
|
|
848
|
+
images: {
|
|
849
|
+
total: number;
|
|
850
|
+
/** Images with neither width and height attributes nor a sized style. */
|
|
851
|
+
missingDimensions: number;
|
|
852
|
+
/** Up to five of them, by URL as written. */
|
|
853
|
+
missingDimensionsExamples: string[];
|
|
854
|
+
/** The first content image before much text (the likely largest paint), when it is lazy-loaded. */
|
|
855
|
+
lazyHero: {
|
|
856
|
+
src: string;
|
|
857
|
+
method: Exclude<SeoPageLazyMethodValue, "none">;
|
|
858
|
+
} | null;
|
|
859
|
+
};
|
|
860
|
+
/** What the browser render found beside the raw HTML; absent on reads made before rendering, null when no HTML was read. */
|
|
861
|
+
render?: SeoPageRenderFacts | null;
|
|
862
|
+
/** What the content rules read from the main text; absent on reads made before it was kept, null when no HTML was read. */
|
|
863
|
+
text?: SeoPageTextFacts | null;
|
|
864
|
+
/** What the optional checks found; absent when none ran. */
|
|
865
|
+
checks?: SeoPageCheckFacts | null;
|
|
866
|
+
}
|
|
867
|
+
/** One sitemap file as the plan read it, for the sitemap hygiene rules. */
|
|
868
|
+
interface SeoSitemapFileFacts {
|
|
869
|
+
/** The file's URL. */
|
|
870
|
+
url: string;
|
|
871
|
+
/** Page URLs it listed. */
|
|
872
|
+
urls: number;
|
|
873
|
+
/** How many of them carried a `<lastmod>`. */
|
|
874
|
+
withLastmod: number;
|
|
875
|
+
/** How many distinct `<lastmod>` values (to the day) they carried. */
|
|
876
|
+
distinctLastmod: number;
|
|
877
|
+
/**
|
|
878
|
+
* How the plan found the file: a `Sitemap:` line of robots.txt, a sitemap index (`declared` when
|
|
879
|
+
* that index was itself named by robots.txt), or a common path tried on every site. Absent on
|
|
880
|
+
* reads made before it was kept, as are the fields below.
|
|
881
|
+
*/
|
|
882
|
+
source?: SeoSitemapSourceValue;
|
|
883
|
+
/** The last hop's status (0 when nothing answered). */
|
|
884
|
+
status?: number;
|
|
885
|
+
/** Why the file yielded no sitemap: not a sitemap at all, an empty answer, or a sitemap without its namespace. */
|
|
886
|
+
invalid?: "not_sitemap" | "empty" | "namespace" | "malformed" | null;
|
|
887
|
+
/** Listed URLs on a host that is not the site's, or outside the file's folder, with a few of them. */
|
|
888
|
+
outOfScope?: {
|
|
889
|
+
count: number;
|
|
890
|
+
examples: string[];
|
|
891
|
+
};
|
|
892
|
+
/** `<lastmod>` values that are not W3C Datetime, with a few of them, and how many lie in the future. */
|
|
893
|
+
lastmodInvalid?: {
|
|
894
|
+
count: number;
|
|
895
|
+
examples: string[];
|
|
896
|
+
};
|
|
897
|
+
lastmodFuture?: number;
|
|
898
|
+
/** URLs carrying a tag Google ignores (`priority`, `changefreq`, a retired image tag). */
|
|
899
|
+
deprecatedTags?: number;
|
|
900
|
+
/** URLs over the image limit, videos missing a required tag, and news entries older than two days. */
|
|
901
|
+
extension?: {
|
|
902
|
+
imagesOver: number;
|
|
903
|
+
videosIncomplete: number;
|
|
904
|
+
newsStale: number;
|
|
905
|
+
};
|
|
906
|
+
/** Listed URLs an earlier sitemap file of the plan listed too. */
|
|
907
|
+
repeated?: number;
|
|
908
|
+
/**
|
|
909
|
+
* The hreflang alternates its entries carry: how many entries carry them, whether the XHTML namespace
|
|
910
|
+
* was missing, and the entries whose set is incomplete (no self entry, or a listed alternate carrying
|
|
911
|
+
* a different set); absent when no entry carries any, and on reads made before they were kept.
|
|
912
|
+
*/
|
|
913
|
+
hreflang?: {
|
|
914
|
+
urls: number;
|
|
915
|
+
namespaceMissing: boolean;
|
|
916
|
+
incomplete: number;
|
|
917
|
+
examples: string[];
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
/** How a plan found a sitemap file. */
|
|
921
|
+
type SeoSitemapSourceValue = "robots" | "index" | "declared_index" | "guess";
|
|
922
|
+
/** The region of the world the platform's crawl server runs in, as a deployment names it. */
|
|
923
|
+
type SeoCrawlRegionValue = "africa" | "asia" | "europe" | "middle_east" | "north_america" | "oceania" | "south_america";
|
|
924
|
+
/** Where a page was fetched from: the member's computer, the server in a region, or the server. */
|
|
925
|
+
type SeoMeasuredFromValue = "computer" | "server" | SeoCrawlRegionValue;
|
|
926
|
+
/** What a request for a page that cannot exist answered: a catch-all answers 200 instead of 404. */
|
|
927
|
+
interface SeoSoft404Probe {
|
|
928
|
+
/** When it was asked (ISO). */
|
|
929
|
+
checkedAt: string;
|
|
930
|
+
/** The probe URL. */
|
|
931
|
+
url: string;
|
|
932
|
+
/** The first hop's status, or 0 when nothing answered. */
|
|
933
|
+
status: number;
|
|
934
|
+
/** The last hop's status, or 0 when nothing answered. */
|
|
935
|
+
finalStatus: number;
|
|
936
|
+
/** The page key the probe landed on after redirects, or null when it did not move. */
|
|
937
|
+
landedOn: string | null;
|
|
938
|
+
/** True when the page it answered with says `noindex` (header or meta); absent otherwise. */
|
|
939
|
+
noindex?: boolean;
|
|
940
|
+
}
|
|
941
|
+
/** Whose robots.txt group a crawl obeys: its own token's (else `*`), or a search engine's. */
|
|
942
|
+
type SeoCrawlRobotsGroupValue = "own" | "googlebot" | "bingbot";
|
|
943
|
+
/**
|
|
944
|
+
* Which crawler the crawl presents itself as: its own honest User-Agent, a search engine's (Google's
|
|
945
|
+
* smartphone or desktop crawler, Bing's), or a member's own string. A preset also chooses the robots.txt
|
|
946
|
+
* group obeyed and the viewport pages render at; on the platform's servers the HTTP User-Agent is
|
|
947
|
+
* always the crawler's own.
|
|
948
|
+
*/
|
|
949
|
+
type SeoCrawlUserAgentPresetValue = "own" | "googlebot_smartphone" | "googlebot_desktop" | "bingbot" | "custom";
|
|
950
|
+
/** One regular expression URL rewrite (RE2 syntax, `$1` in the replacement names a group). */
|
|
951
|
+
interface SeoUrlRewrite {
|
|
952
|
+
pattern: string;
|
|
953
|
+
replace: string;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* One custom JavaScript snippet, run on a member's computer in an isolated world of each rendered page.
|
|
957
|
+
* Only an owner or admin of the project's organization may save one, and it runs only on the computer
|
|
958
|
+
* of such a member who switched snippets on there; who last edited it is kept and shown.
|
|
959
|
+
*/
|
|
960
|
+
interface SeoCustomScript {
|
|
961
|
+
id: string;
|
|
962
|
+
name: string;
|
|
963
|
+
/** The body of an async function whose return value is kept. */
|
|
964
|
+
code: string;
|
|
965
|
+
/** The user who last changed its name or code, or null before that was kept. */
|
|
966
|
+
editedBy: string | null;
|
|
967
|
+
/** That user's name as it was shown then, or null. */
|
|
968
|
+
editedByName: string | null;
|
|
969
|
+
/** When it was last changed (ISO), or null. */
|
|
970
|
+
editedAt: string | null;
|
|
971
|
+
}
|
|
972
|
+
/** One AI prompt run over each page's main text at the audit, charged to the project's payer. */
|
|
973
|
+
interface SeoAiPagePrompt {
|
|
974
|
+
id: string;
|
|
975
|
+
name: string;
|
|
976
|
+
prompt: string;
|
|
977
|
+
}
|
|
978
|
+
/** What only a crawl run on a member's computer does; the platform's servers ignore it. */
|
|
979
|
+
interface SeoCrawlDesktopSettings {
|
|
980
|
+
/** Requests per second to the site; null for the server's polite pace. Never faster than `Crawl-delay`. */
|
|
981
|
+
requestsPerSecond: number | null;
|
|
982
|
+
/** Whether a screenshot of every rendered page is kept on the computer. */
|
|
983
|
+
screenshots: boolean;
|
|
984
|
+
/** Whether every page's raw and rendered HTML is kept on the computer. */
|
|
985
|
+
storeHtml: boolean;
|
|
986
|
+
/** Whether the English text of every page is checked for grammar. */
|
|
987
|
+
grammar: boolean;
|
|
988
|
+
/** The project's custom JavaScript snippets. */
|
|
989
|
+
scripts: SeoCustomScript[];
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* A PROJECT'S CRAWL SETTINGS, set by its members: which URLs are crawled and how a URL is cleaned
|
|
993
|
+
* before it is keyed. Patterns are robots.txt-style (`*` any characters, `$` the end) matched against a
|
|
994
|
+
* URL's path and query, so a pattern can never be slow to match.
|
|
995
|
+
*/
|
|
996
|
+
interface SeoCrawlSettings {
|
|
997
|
+
/** When any is set, only URLs matching one are crawled (the home page always is). */
|
|
998
|
+
include: string[];
|
|
999
|
+
/** URLs matching one are never crawled. */
|
|
1000
|
+
exclude: string[];
|
|
1001
|
+
/** Query parameters dropped from every URL found (a trailing `*` names a prefix, `sort*`). */
|
|
1002
|
+
removeParams: string[];
|
|
1003
|
+
/** Whether every query parameter is dropped. */
|
|
1004
|
+
removeAllParams: boolean;
|
|
1005
|
+
/** Whether URL paths are lowercased. */
|
|
1006
|
+
lowercasePaths: boolean;
|
|
1007
|
+
/**
|
|
1008
|
+
* Whose robots.txt group the crawl obeys. A search engine's group is honoured only on a site the
|
|
1009
|
+
* project verified it owns; otherwise the crawler's own group is obeyed.
|
|
1010
|
+
*/
|
|
1011
|
+
robotsGroup: SeoCrawlRobotsGroupValue;
|
|
1012
|
+
/** The members' own search and extraction patterns, run on every page the crawl reads. */
|
|
1013
|
+
customChecks: SeoCustomCheck[];
|
|
1014
|
+
/**
|
|
1015
|
+
* Whether the audit compares the pages' meaning (an AI embedding of each page's main text), each
|
|
1016
|
+
* page's embedding charged to the project's payer in credits. Off by default.
|
|
1017
|
+
*/
|
|
1018
|
+
semanticChecks: boolean;
|
|
1019
|
+
/** Named URL-pattern groups (`/blog/`, `*\/product/*`) the issues and pages can be filtered by. */
|
|
1020
|
+
segments: SeoCrawlSegment[];
|
|
1021
|
+
/**
|
|
1022
|
+
* For a big site: at most this many pages of one URL pattern (`/blog/*`) are crawled, pages with
|
|
1023
|
+
* search traffic always kept; null crawls every page the cap allows.
|
|
1024
|
+
*/
|
|
1025
|
+
samplePerPattern: number | null;
|
|
1026
|
+
/** Which crawler the crawl presents itself as. */
|
|
1027
|
+
userAgentPreset: SeoCrawlUserAgentPresetValue;
|
|
1028
|
+
/** The member's own User-Agent, for the `custom` preset; null otherwise. */
|
|
1029
|
+
customUserAgent: string | null;
|
|
1030
|
+
/** Regular expression rewrites applied to every URL found, in order, before it is keyed. */
|
|
1031
|
+
urlRewrites: SeoUrlRewrite[];
|
|
1032
|
+
/** Whether the site's subdomains are crawled as part of it (each under its own robots.txt). */
|
|
1033
|
+
subdomainsInternal: boolean;
|
|
1034
|
+
/** How many links deep from the pages found another way the crawl follows; null for no limit. */
|
|
1035
|
+
maxDepth: number | null;
|
|
1036
|
+
/** How long a rendered page is left to run after it settles before it is read, in ms. */
|
|
1037
|
+
renderWaitMs: number;
|
|
1038
|
+
/** AI prompts run over each page's main text at the audit (opt-in: none by default). */
|
|
1039
|
+
aiPrompts: SeoAiPagePrompt[];
|
|
1040
|
+
/**
|
|
1041
|
+
* The audit thresholds the project changed (title widths, little text, slow answer...), by key;
|
|
1042
|
+
* a key left out keeps the tuned default. Only an owner or admin may change them.
|
|
1043
|
+
*/
|
|
1044
|
+
thresholds: Partial<Record<string, number>>;
|
|
1045
|
+
/** What only a crawl on a member's computer does. */
|
|
1046
|
+
desktop: SeoCrawlDesktopSettings;
|
|
1047
|
+
/**
|
|
1048
|
+
* Where the project's scheduled crawls run: on SEOStudio's servers (`cloud`), or on the members' own
|
|
1049
|
+
* server (`own_server`), where `seostudio agent` takes them. A standalone crawl ignores it.
|
|
1050
|
+
*/
|
|
1051
|
+
runner: SeoCrawlRunnerValue;
|
|
1052
|
+
}
|
|
1053
|
+
/** Where a project's scheduled crawls run. */
|
|
1054
|
+
type SeoCrawlRunnerValue = "cloud" | "own_server";
|
|
1055
|
+
/** What a custom pattern looks for: pages that contain it, pages that do not, or values to extract. */
|
|
1056
|
+
type SeoCustomCheckModeValue = "contains" | "absent" | "extract";
|
|
1057
|
+
/**
|
|
1058
|
+
* How a custom pattern is matched against the page's HTML (the rendered page whenever the browser
|
|
1059
|
+
* rendered it): plain text (case-insensitive), a regular expression (RE2 syntax, so a match is never
|
|
1060
|
+
* slow), or a CSS selector.
|
|
1061
|
+
*/
|
|
1062
|
+
type SeoCustomCheckMatchValue = "text" | "regex" | "css";
|
|
1063
|
+
/** One of a project's custom search or extraction patterns. */
|
|
1064
|
+
interface SeoCustomCheck {
|
|
1065
|
+
/** A stable id the results are stored under. */
|
|
1066
|
+
id: string;
|
|
1067
|
+
/** The members' name for it. */
|
|
1068
|
+
name: string;
|
|
1069
|
+
mode: SeoCustomCheckModeValue;
|
|
1070
|
+
match: SeoCustomCheckMatchValue;
|
|
1071
|
+
pattern: string;
|
|
1072
|
+
/** For a CSS extraction, the attribute whose value is extracted instead of the element's text. */
|
|
1073
|
+
attribute: string | null;
|
|
1074
|
+
}
|
|
1075
|
+
/** How one AI agent file of the site answered at a plan. */
|
|
1076
|
+
type SeoAgentFileOutcomeValue = "absent" | "ok" | "server_error" | "soft_404" | "no_h1" | "no_links" | "invalid_json";
|
|
1077
|
+
/**
|
|
1078
|
+
* THE SITE'S FILES FOR AI AGENTS at the last plan: `/llms.txt`, the declared agent files under
|
|
1079
|
+
* `/.well-known/`, and whether the home page answers a request for markdown with markdown.
|
|
1080
|
+
*/
|
|
1081
|
+
interface SeoAgentFilesFacts {
|
|
1082
|
+
/** When they were read (ISO). */
|
|
1083
|
+
checkedAt: string;
|
|
1084
|
+
/** Each file asked, by path, with its status (null when nothing answered) and what it was judged. */
|
|
1085
|
+
files: {
|
|
1086
|
+
path: string;
|
|
1087
|
+
status: number | null;
|
|
1088
|
+
outcome: SeoAgentFileOutcomeValue;
|
|
1089
|
+
}[];
|
|
1090
|
+
/** Whether the home page answered `Accept: text/markdown` with markdown. */
|
|
1091
|
+
markdownNegotiated: boolean;
|
|
1092
|
+
}
|
|
1093
|
+
/** One saved segment: a name and the robots.txt-style patterns a page's URL matches it by. */
|
|
1094
|
+
interface SeoCrawlSegment {
|
|
1095
|
+
name: string;
|
|
1096
|
+
patterns: string[];
|
|
1097
|
+
}
|
|
1098
|
+
/** What an audit changed for one rule. */
|
|
1099
|
+
interface SeoAuditRunChange {
|
|
1100
|
+
/** Issues new to the audit. */
|
|
1101
|
+
found: number;
|
|
1102
|
+
/** Issues the audit no longer found. */
|
|
1103
|
+
fixed: number;
|
|
1104
|
+
/** Issues found again after an earlier audit had them fixed. */
|
|
1105
|
+
regressed: number;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* What a page references: an image, a stylesheet, a script, a font, an icon, a media file (video,
|
|
1109
|
+
* audio, a track), the `og:image`, a request its render could not complete that is none of those
|
|
1110
|
+
* (`other`: data and API requests), a page on another site it links to (`link`), a document it links
|
|
1111
|
+
* to (`document`: a PDF, an office file) on the site or off it, or a spelling of a link to a page of the
|
|
1112
|
+
* site that differs from the URL the crawler reads that page at (`spelling`: a trailing slash, a doubled
|
|
1113
|
+
* slash), asked once without following where it leads.
|
|
1114
|
+
*/
|
|
1115
|
+
type SeoResourceKindValue = "image" | "stylesheet" | "script" | "font" | "icon" | "media" | "og_image" | "other" | "link" | "document" | "spelling";
|
|
1116
|
+
/** How a resource was asked for: a HEAD, or a GET where the HEAD was refused or told too little. */
|
|
1117
|
+
type SeoResourceCheckMethodValue = "head" | "get";
|
|
1118
|
+
/**
|
|
1119
|
+
* Why a resource check got no usable answer. `dns_not_found` is a host that does not exist in DNS
|
|
1120
|
+
* (NXDOMAIN), told apart from a resolver that could not answer for now (`network`).
|
|
1121
|
+
*/
|
|
1122
|
+
type SeoResourceCheckErrorValue = "timeout" | "blocked" | "network" | "too_many_redirects" | "dns_not_found";
|
|
1123
|
+
/**
|
|
1124
|
+
* The caching and compression headers a check of a file a page loads keeps, for the compression and
|
|
1125
|
+
* cache lifetime checks (each cut to 200 characters), and what an image's first bytes said beyond its
|
|
1126
|
+
* pixel size. An outbound link keeps none.
|
|
1127
|
+
*/
|
|
1128
|
+
interface SeoResourceHeaders {
|
|
1129
|
+
cacheControl: string | null;
|
|
1130
|
+
expires: string | null;
|
|
1131
|
+
contentEncoding: string | null;
|
|
1132
|
+
/**
|
|
1133
|
+
* A compressed script or stylesheet's unpacked size (what Googlebot's per-file limit counts), read
|
|
1134
|
+
* just past that limit; absent when it was not measured.
|
|
1135
|
+
*/
|
|
1136
|
+
decodedBytes?: number;
|
|
1137
|
+
/**
|
|
1138
|
+
* Whether an image's first bytes show an animated GIF (one that loops or shows a second frame);
|
|
1139
|
+
* absent when they were not read.
|
|
1140
|
+
*/
|
|
1141
|
+
animated?: boolean;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* One Lighthouse audit of a lab test, as the site audit's `lh_*` rules read it: Lighthouse's own score,
|
|
1145
|
+
* the savings it estimated, and a few examples. Plain data: no English.
|
|
1146
|
+
*/
|
|
1147
|
+
interface SeoLabAudit {
|
|
1148
|
+
/** Lighthouse's score, 0 to 1 (below 0.9 is a failure), or null when it does not apply. */
|
|
1149
|
+
score: number | null;
|
|
1150
|
+
/** The largest metric saving Lighthouse estimated (FCP, LCP, TBT or INP), in ms. */
|
|
1151
|
+
savingsMs: number;
|
|
1152
|
+
/** The bytes Lighthouse estimated could be saved. */
|
|
1153
|
+
savingsBytes: number;
|
|
1154
|
+
/** The audit's measured value, when it has one (main-thread time, a chain's length, the CLS). */
|
|
1155
|
+
value: number | null;
|
|
1156
|
+
/** The checks of a checklist audit that failed, by Lighthouse's key. */
|
|
1157
|
+
failedChecks: string[];
|
|
1158
|
+
/** A few URLs it names, and how many it names in all. */
|
|
1159
|
+
urls: string[];
|
|
1160
|
+
urlCount: number;
|
|
1161
|
+
/** A few elements it names, as CSS selectors. */
|
|
1162
|
+
elements: string[];
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
export type { SeoAgentFileOutcomeValue, SeoAgentFilesFacts, SeoAiPagePrompt, SeoAuditRunChange, SeoCrawlDesktopSettings, SeoCrawlRegionValue, SeoCrawlRobotsGroupValue, SeoCrawlRunnerValue, SeoCrawlSegment, SeoCrawlSettings, SeoCrawlUserAgentPresetValue, SeoCustomCheck, SeoCustomCheckMatchValue, SeoCustomCheckModeValue, SeoCustomScript, SeoHostProbe, SeoHostProbes, SeoHreflangProbe, SeoLabAudit, SeoMeasuredFromValue, SeoPageA11yFacts, SeoPageAmpError, SeoPageAmpFacts, SeoPageAuditFacts, SeoPageBadLinkKindValue, SeoPageCheckFacts, SeoPageCustomResult, SeoPageDesktopView, SeoPageDomFacts, SeoPageDomImage, SeoPageFetchModeValue, SeoPageGrammarFacts, SeoPageGrammarIssue, SeoPageHeadFacts, SeoPageHeading, SeoPageHreflang, SeoPageIssueSeverityValue, SeoPageJsonLdBlock, SeoPageLazyMethodValue, SeoPageLinkPlacementValue, SeoPageMarkedValues, SeoPageMarkupFacts, SeoPageMarkupImage, SeoPageMarkupItem, SeoPageMarkupText, SeoPageRedirectHop, SeoPageRenderChange, SeoPageRenderFacts, SeoPageRenderOutcomeValue, SeoPageRenderResourceFailure, SeoPageRobotsGroup, SeoPageScriptResult, SeoPageSpellingFacts, SeoPageTextFacts, SeoPageViewport, SeoResourceCheckErrorValue, SeoResourceCheckMethodValue, SeoResourceHeaders, SeoResourceKindValue, SeoRobotsFetchFacts, SeoRobotsFetchOutcomeValue, SeoSitePlatformValue, SeoSitemapFileFacts, SeoSitemapSourceValue, SeoSoft404Probe, SeoTlsFacts, SeoUrlRewrite };
|