@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/types.d.ts
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { SeoPageRenderOutcomeValue, SeoPageRenderResourceFailure, SeoPageDomFacts, SeoPageA11yFacts, SeoPageScriptResult, SeoPageRedirectHop, SeoPageHeading, SeoPageHreflang, SeoPageLinkPlacementValue, SeoPageMarkupFacts, SeoTlsFacts, SeoCrawlRegionValue } from './facts.js';
|
|
2
|
+
import { SeoHostGate } from './host-gate.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The shapes the crawler and the audit share: what an extractor reads from a page, what a fetch
|
|
6
|
+
* answered, what a browser render returns, and what a crawl reaches outside itself.
|
|
7
|
+
*
|
|
8
|
+
* Types only: this file loads nothing at runtime.
|
|
9
|
+
*/
|
|
10
|
+
/** A calendar day in UTC, `YYYY-MM-DD`. */
|
|
11
|
+
type IsoDate = string;
|
|
12
|
+
/** One link found on a page. */
|
|
13
|
+
interface ExtractedLink {
|
|
14
|
+
/** The absolute URL the link points at. */
|
|
15
|
+
href: string;
|
|
16
|
+
/** The anchor text, else an image's alt, else the link's accessible label; whitespace collapsed. */
|
|
17
|
+
anchor: string;
|
|
18
|
+
placement: SeoPageLinkPlacementValue;
|
|
19
|
+
nofollow: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Everything the extractor reads from one HTML page. */
|
|
22
|
+
interface ExtractedPage {
|
|
23
|
+
title: string;
|
|
24
|
+
metaDescription: string;
|
|
25
|
+
h1: string;
|
|
26
|
+
h1Count: number;
|
|
27
|
+
/** H1s holding neither text nor an image with alt text; absent on a reading made before it was counted. */
|
|
28
|
+
h1Empty?: number;
|
|
29
|
+
headings: SeoPageHeading[];
|
|
30
|
+
/** The canonical link's absolute URL, or empty. */
|
|
31
|
+
canonical: string;
|
|
32
|
+
/** The `robots` (and `googlebot`) meta directives, lowercased and joined, or empty. */
|
|
33
|
+
metaRobots: string;
|
|
34
|
+
lang: string;
|
|
35
|
+
viewport: string;
|
|
36
|
+
schemaTypes: string[];
|
|
37
|
+
hreflang: SeoPageHreflang[];
|
|
38
|
+
hasOpenGraph: boolean;
|
|
39
|
+
hasTwitterCard: boolean;
|
|
40
|
+
images: number;
|
|
41
|
+
imagesMissingAlt: number;
|
|
42
|
+
/** `YYYY-MM-DD`, or null when the page states none. */
|
|
43
|
+
datePublished: IsoDate | null;
|
|
44
|
+
dateModified: IsoDate | null;
|
|
45
|
+
links: ExtractedLink[];
|
|
46
|
+
/** The page's main text, boilerplate stripped, one block per line. */
|
|
47
|
+
mainText: string;
|
|
48
|
+
wordCount: number;
|
|
49
|
+
/** Whether the page looks like a bot challenge rather than the page itself. */
|
|
50
|
+
botWall: boolean;
|
|
51
|
+
/** What the site audit reads beyond the snapshot fields. */
|
|
52
|
+
audit: ExtractedAuditFacts;
|
|
53
|
+
/**
|
|
54
|
+
* What the markup states, read by a standards-following parser (`./extract-markup`); absent on a
|
|
55
|
+
* reading made by an app from before it was read.
|
|
56
|
+
*/
|
|
57
|
+
markup?: ExtractedMarkup;
|
|
58
|
+
}
|
|
59
|
+
/** What a subresource is, for the resource checks. */
|
|
60
|
+
type ExtractedResourceKind = "image" | "stylesheet" | "script" | "font" | "icon" | "media" | "og_image";
|
|
61
|
+
/** One subresource a page references: absolute http(s) URL, as resolved against the page. */
|
|
62
|
+
interface ExtractedResource {
|
|
63
|
+
url: string;
|
|
64
|
+
kind: ExtractedResourceKind;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The markup facts of one read, and the subresources it references (handed to the resource checks,
|
|
68
|
+
* never stored with the page).
|
|
69
|
+
*/
|
|
70
|
+
interface ExtractedMarkup extends SeoPageMarkupFacts {
|
|
71
|
+
resources: ExtractedResource[];
|
|
72
|
+
}
|
|
73
|
+
/** The headers of a fetch the site audit reads. */
|
|
74
|
+
interface FetchedAuditHeaders {
|
|
75
|
+
strictTransportSecurity: string | null;
|
|
76
|
+
contentSecurityPolicy: string | null;
|
|
77
|
+
xContentTypeOptions: string | null;
|
|
78
|
+
xFrameOptions: string | null;
|
|
79
|
+
referrerPolicy: string | null;
|
|
80
|
+
/** The raw `Link` header. */
|
|
81
|
+
link: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* `Content-Type`, `Content-Encoding`, `Cache-Control`, `Content-Language` and `Refresh`; every fetch
|
|
84
|
+
* reads them, a reading from an app from before they were read lacks them.
|
|
85
|
+
*/
|
|
86
|
+
contentType?: string | null;
|
|
87
|
+
contentEncoding?: string | null;
|
|
88
|
+
cacheControl?: string | null;
|
|
89
|
+
contentLanguage?: string | null;
|
|
90
|
+
refresh?: string | null;
|
|
91
|
+
}
|
|
92
|
+
/** What the extractor reads for the site audit alone. */
|
|
93
|
+
interface ExtractedAuditFacts {
|
|
94
|
+
/** Every `<link rel="canonical">` target, absolute, in document order. */
|
|
95
|
+
canonicals: string[];
|
|
96
|
+
/** The JSON-LD blocks' raw text, in document order. */
|
|
97
|
+
jsonLd: string[];
|
|
98
|
+
/** `http:` subresources: scripts, frames, stylesheets, objects (active) and media (passive). */
|
|
99
|
+
mixedActive: string[];
|
|
100
|
+
mixedPassive: string[];
|
|
101
|
+
/** Images outside `<noscript>`. */
|
|
102
|
+
images: number;
|
|
103
|
+
/** Of them, the ones with no size (no width and height attributes, no sized style). */
|
|
104
|
+
unsizedImages: string[];
|
|
105
|
+
/** The first content image before much text, when it is lazy-loaded, and how. */
|
|
106
|
+
lazyHero: {
|
|
107
|
+
src: string;
|
|
108
|
+
method: "native" | "javascript";
|
|
109
|
+
} | null;
|
|
110
|
+
}
|
|
111
|
+
/** What one fetch (redirects followed) answered. */
|
|
112
|
+
interface FetchedResource {
|
|
113
|
+
requestedUrl: string;
|
|
114
|
+
/** Where the last hop landed. */
|
|
115
|
+
finalUrl: string;
|
|
116
|
+
/** The FIRST hop's status (a redirecting URL is a redirect), or 0 when nothing answered. */
|
|
117
|
+
status: number;
|
|
118
|
+
/** The last hop's status. */
|
|
119
|
+
finalStatus: number;
|
|
120
|
+
/** Every hop, the final one included. */
|
|
121
|
+
chain: SeoPageRedirectHop[];
|
|
122
|
+
contentType: string | null;
|
|
123
|
+
xRobotsTag: string | null;
|
|
124
|
+
/** The last hop's headers the site audit reads, null where absent (all null when nothing answered). */
|
|
125
|
+
auditHeaders: FetchedAuditHeaders;
|
|
126
|
+
/** The last hop's body, capped; null when nothing was read, or when a sink took it as it arrived. */
|
|
127
|
+
body: Uint8Array | null;
|
|
128
|
+
truncated: boolean;
|
|
129
|
+
sizeBytes: number;
|
|
130
|
+
/** How long the last hop took, its body read (the whole chain's time when it failed). */
|
|
131
|
+
responseMs: number;
|
|
132
|
+
error: "timeout" | "blocked" | "network" | "too_many_redirects" | null;
|
|
133
|
+
/** The last hop's `Retry-After` header, when it sent one (a 429 or 503 asking for a pause). */
|
|
134
|
+
retryAfter?: string | null;
|
|
135
|
+
/** Whether a 2xx body holds an HTML document its `Content-Type` does not name as HTML; absent when not. */
|
|
136
|
+
htmlUnlabelled?: boolean;
|
|
137
|
+
/** On HTML over what Googlebot reads, what its cut leaves out (`./reading` `htmlCutOf`); absent when not over. */
|
|
138
|
+
htmlCut?: {
|
|
139
|
+
head: boolean;
|
|
140
|
+
mainText: boolean;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* On a 403 to the browser User-Agent naming the crawler a bot, whether the site served the same page
|
|
144
|
+
* to the crawler's own plain User-Agent (`./reading` `answersPlainAgent`): it refuses the string,
|
|
145
|
+
* not the crawler's address. Absent otherwise.
|
|
146
|
+
*/
|
|
147
|
+
botAgentRefused?: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** A `fetch`-shaped transport with the guard's timeout argument (`safeFetch` in production). */
|
|
150
|
+
type SeoCrawlFetcher = (url: string, init: RequestInit, timeoutMs: number) => Promise<Response>;
|
|
151
|
+
/** What a browser is asked to render: the page, and the raw HTML its own fetch already read. */
|
|
152
|
+
interface SeoRenderRequest {
|
|
153
|
+
/** The page's URL (where its raw fetch landed, on the page itself). */
|
|
154
|
+
url: string;
|
|
155
|
+
/** The raw HTML the crawler read: the browser renders exactly this document, never fetching it again. */
|
|
156
|
+
html: string;
|
|
157
|
+
/** The raw answer's status (2xx). */
|
|
158
|
+
status: number;
|
|
159
|
+
/** The raw answer's `Content-Security-Policy`, so the page's scripts run under the policy it set. */
|
|
160
|
+
contentSecurityPolicy: string | null;
|
|
161
|
+
/** The crawler's own User-Agent; the browser sends it inside a phone Chrome's. */
|
|
162
|
+
userAgent: string;
|
|
163
|
+
/**
|
|
164
|
+
* Whether a subresource may be fetched: the site's robots.txt for its own hosts (Google obeys it for
|
|
165
|
+
* scripts, styles and data too). A refused script, style or data request is recorded as blocked.
|
|
166
|
+
*/
|
|
167
|
+
allowResource: ((url: string) => boolean) | null;
|
|
168
|
+
/**
|
|
169
|
+
* Waits for the request's turn before a subresource is fetched: the per-host pace every crawl of the
|
|
170
|
+
* site's own host shares (the server's). Absent where a crawl paces only itself (the desktop app).
|
|
171
|
+
*/
|
|
172
|
+
paceResource?: (url: string) => Promise<void>;
|
|
173
|
+
/** The device the page is rendered as: a phone (the default, as Google renders) or a desktop. */
|
|
174
|
+
device?: "mobile" | "desktop";
|
|
175
|
+
/** Whether a phone render runs the in-page probe once the DOM is read (default true). */
|
|
176
|
+
probe?: boolean;
|
|
177
|
+
/** Which accessibility rules (axe-core) run on a phone render: the search-relevant subset, all, or none. */
|
|
178
|
+
accessibility?: "seo" | "all" | null;
|
|
179
|
+
/**
|
|
180
|
+
* The site's sign-in headers for a request the page makes: what the members set for the project's
|
|
181
|
+
* own hosts, null elsewhere (`./site-access`). Absent when the site asks for none.
|
|
182
|
+
*/
|
|
183
|
+
siteHeaders?: (url: string) => Record<string, string> | null;
|
|
184
|
+
/** Whether the rendered page is also captured as a screenshot (a single page analysis). */
|
|
185
|
+
screenshot?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* A screenshot for a page's content (the content writer's images): the page's images are loaded,
|
|
188
|
+
* its cookie banner is dismissed (reject), the shot is cropped to an element or region and the boxes
|
|
189
|
+
* of the elements to mark and to blur are measured. The page is rendered at twice the pixel density.
|
|
190
|
+
*/
|
|
191
|
+
capture?: SeoRenderCapture;
|
|
192
|
+
/**
|
|
193
|
+
* How long the page is left to run once it settled before its DOM is read, in ms: the members' render
|
|
194
|
+
* wait, kept within the render's time limit. None by default.
|
|
195
|
+
*/
|
|
196
|
+
waitMs?: number;
|
|
197
|
+
/**
|
|
198
|
+
* The project's custom JavaScript snippets, run once the DOM is read in an isolated world the page's
|
|
199
|
+
* own scripts cannot reach, each bounded in time and output (a member's computer only).
|
|
200
|
+
*/
|
|
201
|
+
scripts?: readonly SeoRenderScript[];
|
|
202
|
+
/**
|
|
203
|
+
* Whether the browser asks for the document itself, as a visitor's browser does (its own headers,
|
|
204
|
+
* the page's cookies, a bot check's script let run and its reload followed), every request still
|
|
205
|
+
* through the guard: for a page whose plain fetch a bot check refused. `html` and `status` are then
|
|
206
|
+
* not read; the answer carries the document's own status (`documentStatus`).
|
|
207
|
+
*/
|
|
208
|
+
fetchDocument?: boolean;
|
|
209
|
+
}
|
|
210
|
+
/** One custom JavaScript snippet as the render runs it: its id and its code. */
|
|
211
|
+
interface SeoRenderScript {
|
|
212
|
+
id: string;
|
|
213
|
+
code: string;
|
|
214
|
+
}
|
|
215
|
+
/** What a browser answered for one page, before the extractor reads it. */
|
|
216
|
+
interface SeoBrowserRender {
|
|
217
|
+
outcome: SeoPageRenderOutcomeValue;
|
|
218
|
+
/** Why it failed, or null. */
|
|
219
|
+
error: string | null;
|
|
220
|
+
/** The rendered DOM, serialized; null when the render failed. */
|
|
221
|
+
html: string | null;
|
|
222
|
+
/** The URL of the document the browser ended on (a script may navigate away). */
|
|
223
|
+
finalUrl: string;
|
|
224
|
+
renderMs: number;
|
|
225
|
+
/** Uncaught script errors and console errors, cut short. */
|
|
226
|
+
consoleErrors: string[];
|
|
227
|
+
/** Scripts, stylesheets and data requests the page could not load. */
|
|
228
|
+
failedResources: SeoPageRenderResourceFailure[];
|
|
229
|
+
/** Requests the guard refused (a private address, a scheme other than https): never sent. */
|
|
230
|
+
blockedRequests: number;
|
|
231
|
+
/**
|
|
232
|
+
* Requests a host refused for the asker's rate limit (a 429, or a 403 saying no request is left):
|
|
233
|
+
* the errors a page logs for them are the crawl's own doing. Absent from a render made before it.
|
|
234
|
+
*/
|
|
235
|
+
rateLimitedRequests?: number;
|
|
236
|
+
/** What the in-page probe measured, or null when it did not run (or could not, in its time). */
|
|
237
|
+
dom?: SeoPageDomFacts | null;
|
|
238
|
+
/** What the accessibility checks found, or null when they did not run. */
|
|
239
|
+
a11y?: SeoPageA11yFacts | null;
|
|
240
|
+
/** The screenshot asked for: a JPEG, base64; null when it could not be taken. */
|
|
241
|
+
screenshot?: string | null;
|
|
242
|
+
/** What the custom JavaScript snippets answered, when any ran. */
|
|
243
|
+
scripts?: SeoPageScriptResult[] | null;
|
|
244
|
+
/** The content screenshot asked for (`capture`), or null when it could not be taken. */
|
|
245
|
+
capture?: SeoRenderCaptureResult | null;
|
|
246
|
+
/**
|
|
247
|
+
* With `fetchDocument`: the status of the last document the browser got for the page (after its
|
|
248
|
+
* redirects and a bot check's reload), or null when none answered.
|
|
249
|
+
*/
|
|
250
|
+
documentStatus?: number | null;
|
|
251
|
+
}
|
|
252
|
+
/** An element a content screenshot looks for: by CSS selector, or by the text it holds. */
|
|
253
|
+
interface SeoCaptureTarget {
|
|
254
|
+
selector?: string;
|
|
255
|
+
text?: string;
|
|
256
|
+
}
|
|
257
|
+
/** What a content screenshot captures. */
|
|
258
|
+
interface SeoRenderCapture {
|
|
259
|
+
/** The element the shot is cropped to (with a margin); the top of the page when null. */
|
|
260
|
+
focus: SeoCaptureTarget | null;
|
|
261
|
+
/** The elements whose boxes are measured to mark them. */
|
|
262
|
+
highlights: SeoCaptureTarget[];
|
|
263
|
+
/**
|
|
264
|
+
* CSS selectors of elements to blur. Emails, phone numbers and filled form fields are always measured
|
|
265
|
+
* to blur. A fixed or sticky element a selector names (an overlay over the page) is hidden instead.
|
|
266
|
+
*/
|
|
267
|
+
mask: string[];
|
|
268
|
+
/** Capture the whole page (capped) rather than the first screen, when there is no focus. */
|
|
269
|
+
fullPage: boolean;
|
|
270
|
+
/** The consent script (DuckDuckGo autoconsent, reject) run before the shot; null runs only the built-in dismissal. */
|
|
271
|
+
consentScript: string | null;
|
|
272
|
+
/** Also read the site's logo and brand colours (the featured template's draft). */
|
|
273
|
+
brand: boolean;
|
|
274
|
+
/**
|
|
275
|
+
* The visitor the page is shown to: its locale (e.g. `en-US`), its time zone and the `Accept-Language`
|
|
276
|
+
* it sends, so prices, dates and words show as in the project's market. The crawler's own locale
|
|
277
|
+
* (en-US, UTC, no language asked for) when omitted.
|
|
278
|
+
*/
|
|
279
|
+
visitor?: {
|
|
280
|
+
locale: string;
|
|
281
|
+
timezone: string;
|
|
282
|
+
acceptLanguage: string;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/** A rectangle in a content screenshot's own pixels. */
|
|
286
|
+
interface SeoCaptureBox {
|
|
287
|
+
x: number;
|
|
288
|
+
y: number;
|
|
289
|
+
width: number;
|
|
290
|
+
height: number;
|
|
291
|
+
}
|
|
292
|
+
/** A fixed or sticky element that lies over the focus or a marked element as a visitor sees the page. */
|
|
293
|
+
interface SeoCaptureCover {
|
|
294
|
+
/** What it covers: the focus, or the index of the marked element. */
|
|
295
|
+
target: "focus" | number;
|
|
296
|
+
/** A CSS selector of the covering element (its id, else its tag and first classes). */
|
|
297
|
+
selector: string;
|
|
298
|
+
/** The start of its text, to recognise it. */
|
|
299
|
+
text: string;
|
|
300
|
+
}
|
|
301
|
+
/** What a content screenshot answered. */
|
|
302
|
+
interface SeoRenderCaptureResult {
|
|
303
|
+
/** The PNG, base64; null when no shot could be taken. */
|
|
304
|
+
png: string | null;
|
|
305
|
+
width: number;
|
|
306
|
+
height: number;
|
|
307
|
+
/** Whether the focus element was found (false: the shot is the first screen). */
|
|
308
|
+
focusFound: boolean;
|
|
309
|
+
/** Each highlight's box in the shot, in order; null when it was not found or is outside the shot. */
|
|
310
|
+
highlights: (SeoCaptureBox | null)[];
|
|
311
|
+
/** The boxes to blur. */
|
|
312
|
+
masks: SeoCaptureBox[];
|
|
313
|
+
/** What happened to a consent banner. */
|
|
314
|
+
consent: "dismissed" | "none" | "failed";
|
|
315
|
+
/** The fixed or sticky elements over the focus or a marked element (the proof may be hidden under them). */
|
|
316
|
+
covered: SeoCaptureCover[];
|
|
317
|
+
/** The site's logo and colours, when asked for. */
|
|
318
|
+
brand: {
|
|
319
|
+
logos: string[];
|
|
320
|
+
themeColor: string | null;
|
|
321
|
+
cssColors: {
|
|
322
|
+
name: string;
|
|
323
|
+
value: string;
|
|
324
|
+
}[];
|
|
325
|
+
ctaColors: string[];
|
|
326
|
+
} | null;
|
|
327
|
+
}
|
|
328
|
+
/** Renders one page in a browser. Never throws: a failure is an outcome. */
|
|
329
|
+
type SeoPageRenderer = (request: SeoRenderRequest) => Promise<SeoBrowserRender>;
|
|
330
|
+
/**
|
|
331
|
+
* Reads the TLS certificate and handshake of a host (the host's own probe). Never throws: null
|
|
332
|
+
* when the host is refused by the guard or nothing answered.
|
|
333
|
+
*/
|
|
334
|
+
type SeoTlsProbe = (host: string) => Promise<SeoTlsFacts | null>;
|
|
335
|
+
/**
|
|
336
|
+
* Reads the status and `Location` of one plain `http://` URL through the guard, redirects never
|
|
337
|
+
* followed (`safePlainHttpGet` in production). Throws on failure, as the guarded transport does.
|
|
338
|
+
*/
|
|
339
|
+
type SeoHttpProbe = (url: string, headers: Record<string, string>, timeoutMs: number) => Promise<{
|
|
340
|
+
status: number;
|
|
341
|
+
location: string | null;
|
|
342
|
+
}>;
|
|
343
|
+
/** What the crawler reaches outside itself, injectable so tests never touch the network. */
|
|
344
|
+
interface SeoCrawlDeps {
|
|
345
|
+
/** The guarded transport; every hop of every redirect goes through it. */
|
|
346
|
+
fetch: SeoCrawlFetcher;
|
|
347
|
+
/** The browser every page is rendered in, its every request through the same guard. */
|
|
348
|
+
render: SeoPageRenderer;
|
|
349
|
+
now: () => Date;
|
|
350
|
+
sleep: (ms: number) => Promise<void>;
|
|
351
|
+
/** The TLS read of the site's host at each plan the server makes; without one the last read stands. */
|
|
352
|
+
tls?: SeoTlsProbe;
|
|
353
|
+
/**
|
|
354
|
+
* The plain-http status read for the host variant checks at each plan the server makes; without one
|
|
355
|
+
* nothing is read over http and the last reads stand.
|
|
356
|
+
*/
|
|
357
|
+
httpProbe?: SeoHttpProbe;
|
|
358
|
+
/**
|
|
359
|
+
* The pace every crawl of a host shares (Redis on the server). Without one, a crawl only pauses
|
|
360
|
+
* between its own requests.
|
|
361
|
+
*/
|
|
362
|
+
hostGate?: SeoHostGate;
|
|
363
|
+
/**
|
|
364
|
+
* The region of the world the server's crawl runs in, as the deployment names it; without one, or
|
|
365
|
+
* when it names none, a response time says only that the server measured it.
|
|
366
|
+
*/
|
|
367
|
+
region?: () => Promise<SeoCrawlRegionValue | undefined>;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export type { ExtractedAuditFacts, ExtractedLink, ExtractedMarkup, ExtractedPage, ExtractedResource, ExtractedResourceKind, FetchedAuditHeaders, FetchedResource, IsoDate, SeoBrowserRender, SeoCaptureBox, SeoCaptureCover, SeoCaptureTarget, SeoCrawlDeps, SeoCrawlFetcher, SeoHttpProbe, SeoPageRenderer, SeoRenderCapture, SeoRenderCaptureResult, SeoRenderRequest, SeoRenderScript, SeoTlsProbe };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@seostudio/audit-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The SEOStudio site audit engine: a polite crawler and its audit rules, with no database.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/DigitalFrontLLP/SEOStudio.git",
|
|
9
|
+
"directory": "packages/audit-engine"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://seostudio.com",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"seo",
|
|
14
|
+
"site-audit",
|
|
15
|
+
"crawler",
|
|
16
|
+
"seo-audit"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./reading": {
|
|
25
|
+
"types": "./dist/reading.d.ts",
|
|
26
|
+
"default": "./dist/reading.js"
|
|
27
|
+
},
|
|
28
|
+
"./resource-check": {
|
|
29
|
+
"types": "./dist/resource-check.d.ts",
|
|
30
|
+
"default": "./dist/resource-check.js"
|
|
31
|
+
},
|
|
32
|
+
"./facts": {
|
|
33
|
+
"types": "./dist/facts.d.ts",
|
|
34
|
+
"default": "./dist/facts.js"
|
|
35
|
+
},
|
|
36
|
+
"./types": {
|
|
37
|
+
"types": "./dist/types.d.ts",
|
|
38
|
+
"default": "./dist/types.js"
|
|
39
|
+
},
|
|
40
|
+
"./host-gate": {
|
|
41
|
+
"types": "./dist/host-gate.d.ts",
|
|
42
|
+
"default": "./dist/host-gate.js"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
],
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22.19.0"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"sideEffects": false,
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"css-select": "^7.0.0",
|
|
60
|
+
"domhandler": "^6.0.1",
|
|
61
|
+
"franc": "^6.2.0",
|
|
62
|
+
"htmlparser2": "^12.0.0",
|
|
63
|
+
"nspell": "^2.1.5",
|
|
64
|
+
"parse5": "^8.0.1",
|
|
65
|
+
"re2js": "^2.8.6",
|
|
66
|
+
"undici": "^8.12.3",
|
|
67
|
+
"zod": "^4.6.7"
|
|
68
|
+
},
|
|
69
|
+
"types": "./dist/index.d.ts"
|
|
70
|
+
}
|