@forkpoint/agent-lighthouse-core 2.0.0 → 3.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/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Dispatcher } from 'undici';
1
2
  import { CheerioAPI } from 'cheerio';
2
3
  import { z } from 'zod';
3
4
 
@@ -41,6 +42,19 @@ interface FetchResult {
41
42
  contentLength: number;
42
43
  /** Raw response bytes. Present only when the request asked for `binary`. */
43
44
  bytes?: Uint8Array;
45
+ /**
46
+ * Every redirect hop the fetcher walked, in order. Absent when the caller
47
+ * disabled redirects or the response was not a redirect.
48
+ *
49
+ * `finalUrl` alone cannot say whether a host change was permanent. A scan
50
+ * has to tell a domain migration (301/308) from a temporary hop to somebody
51
+ * else's domain, so the status of each hop is kept.
52
+ */
53
+ redirectChain?: Array<{
54
+ status: number;
55
+ from: string;
56
+ to: string;
57
+ }>;
44
58
  error?: string;
45
59
  }
46
60
  /**
@@ -48,7 +62,44 @@ interface FetchResult {
48
62
  * Use before fetching any URL extracted from scanned site content.
49
63
  */
50
64
  declare function isSafeUrl(url: string): Promise<boolean>;
51
- declare function createFetcher(): {
65
+ interface FetcherOptions {
66
+ /**
67
+ * undici dispatcher for every request this fetcher makes.
68
+ *
69
+ * Omitted, requests go through a shared `new Agent()` whose per-origin
70
+ * connection count is unlimited — right for a site owner scanning their own
71
+ * site, who wants the scan over quickly. A caller scanning origins that did
72
+ * not invite it passes a bounded agent (`new Agent({ connections: 2 })`) so
73
+ * one scan cannot open 28 sockets at once against a stranger's WAF.
74
+ */
75
+ dispatcher?: Dispatcher;
76
+ /**
77
+ * How many requests this fetcher may have in flight at once.
78
+ *
79
+ * Omitted, there is no ceiling and every request is issued as it arrives.
80
+ *
81
+ * A bounded dispatcher alone is not enough for a caller that wants one, and
82
+ * the difference is what the clocks measure. `Agent({ connections: 2 })`
83
+ * accepts all 26 root-file requests the scan fires in one `Promise.all` and
84
+ * queues 24 of them inside undici — but the per-request deadline and the
85
+ * `ttfbMs` clock both start when `fetch()` is called, so an origin averaging
86
+ * 800 ms per file would have its tail requests time out on the scanner's own
87
+ * queue and be reported as unreachable. Waiting here instead holds a request
88
+ * outside both clocks until it can actually be issued, so what they measure
89
+ * is the origin.
90
+ */
91
+ maxConcurrent?: number;
92
+ }
93
+ /**
94
+ * A dispatcher that opens at most `connections` sockets per origin.
95
+ *
96
+ * The scanner's own default has no such ceiling, and for a site owner scanning
97
+ * their own site that is the right trade. A caller scanning origins that did
98
+ * not invite it wants the ceiling, and this saves it from taking a direct
99
+ * dependency on undici to express one line of politeness.
100
+ */
101
+ declare function boundedDispatcher(connections: number): Dispatcher;
102
+ declare function createFetcher(fetcherOptions?: FetcherOptions): {
52
103
  fetch: (options: FetchOptions) => Promise<FetchResult>;
53
104
  };
54
105
 
@@ -154,7 +205,25 @@ interface AuditMeta {
154
205
  tier?: AuditTier;
155
206
  /** Repo-relative path to the audit's evidence dossier. */
156
207
  dossier?: string;
208
+ /**
209
+ * Which classes of scan evidence this audit needs to say anything true.
210
+ *
211
+ * Declared per audit rather than inferred at runtime, and checked against
212
+ * what the source actually reads by `scripts/check-requires.mjs`. An audit
213
+ * that reads the sampled pages — directly or through a page-fed gatherer —
214
+ * needs all four keys; one that reads only root files needs the origin to
215
+ * have answered. The deliberate disagreements are the audits whose subject
216
+ * *is* the missing evidence, and they are listed in that script.
217
+ */
218
+ requires?: EvidenceKey[];
157
219
  }
220
+ /**
221
+ * A class of evidence a scan either obtained or did not.
222
+ *
223
+ * Declared here rather than in `scan-evidence.ts` so `AuditMeta` can name it
224
+ * without the audit layer importing the scan layer.
225
+ */
226
+ type EvidenceKey = 'origin-reachable' | 'unblocked-fetches' | 'rendered-body' | 'sample-adequate';
158
227
  interface AuditResult {
159
228
  status: CheckStatus;
160
229
  score: number;
@@ -231,12 +300,31 @@ interface CategoryResult {
231
300
  failCount: number;
232
301
  }
233
302
  type ScoreTier = 'agent-ready' | 'partially-ready' | 'needs-work' | 'not-ready';
303
+ /**
304
+ * Whether a verdict about this site can mean anything, and what is missing.
305
+ *
306
+ * A scan that fetched nothing still produced a number before this existed:
307
+ * `ridge.com` scored 43 and `westontable.com` 37 on scans that read no page,
308
+ * against a real store's 51. The numbers overlap, so a reader could not
309
+ * separate "mediocre site" from "we never saw this site". When `judgeable` is
310
+ * false the report carries no score at all — not a zero, not a low number.
311
+ */
312
+ interface ScanValidity {
313
+ judgeable: boolean;
314
+ evidence: Record<EvidenceKey, boolean>;
315
+ reasons: Partial<Record<EvidenceKey, string>>;
316
+ /** Why the score was suppressed, when it was. */
317
+ unscoredReason?: string;
318
+ }
234
319
  interface ScanReport {
235
320
  scanId: string;
236
321
  url: string;
237
322
  domain: string;
238
- overallScore: number;
239
- scoreTier: ScoreTier;
323
+ /** Null when the scan obtained too little evidence to judge the site. */
324
+ overallScore: number | null;
325
+ /** Null exactly when `overallScore` is. */
326
+ scoreTier: ScoreTier | null;
327
+ scanValidity?: ScanValidity;
240
328
  summary?: string;
241
329
  categories: CategoryResult[];
242
330
  topPasses: CheckResult[];
@@ -269,6 +357,39 @@ interface ReadinessVitals {
269
357
  technical: number;
270
358
  }
271
359
 
360
+ /**
361
+ * What a scan actually obtained, decided once, before any audit runs.
362
+ *
363
+ * An audit that runs on evidence the scan never got reports a verdict about
364
+ * the scanner, not about the site: "no structured data" when the fetch was
365
+ * refused, "0 words" when the origin never answered. This module names the
366
+ * classes of evidence a scan can be missing so those audits can be skipped
367
+ * with the reason attached instead of answering blind.
368
+ *
369
+ * Pure: no network, no IO. It reads what the orchestrator already has.
370
+ */
371
+
372
+ interface ScanEvidence {
373
+ met: Record<EvidenceKey, boolean>;
374
+ /** One sentence per unmet key, shown in the `na` stub and the trace. */
375
+ reasons: Partial<Record<EvidenceKey, string>>;
376
+ /** Page URL to "the served HTML carried text a non-JS consumer can read". */
377
+ renderedByPage: Record<string, boolean>;
378
+ usablePageTypes: Set<PageType>;
379
+ /** False when no verdict about this site can mean anything. */
380
+ judgeable: boolean;
381
+ }
382
+ interface ScanEvidenceInput {
383
+ requestedUrl: string;
384
+ homepageResult: FetchResult;
385
+ pages: PageContext[];
386
+ rootFiles: Record<string, FetchResult>;
387
+ wafProtection: WafProtection | null;
388
+ }
389
+ declare function buildScanEvidence(input: ScanEvidenceInput): ScanEvidence;
390
+ /** All requirements met. For test harnesses not exercising the gate. */
391
+ declare function allEvidenceMet(): ScanEvidence;
392
+
272
393
  interface A11yNodeFinding {
273
394
  target: string;
274
395
  summary: string;
@@ -316,6 +437,14 @@ interface CheckContext {
316
437
  baseUrl: string;
317
438
  fetch: (options: FetchOptions) => Promise<FetchResult>;
318
439
  wafProtection?: WafProtection;
440
+ /**
441
+ * What the scan actually obtained, decided once before any audit ran.
442
+ *
443
+ * Required, not optional. An optional field fails open, and a caller that
444
+ * forgets is exactly the silent-nothing verdict this exists to remove. Test
445
+ * harnesses that do not exercise the gate pass `allEvidenceMet()`.
446
+ */
447
+ evidence: ScanEvidence;
319
448
  }
320
449
  type CheckFn = (ctx: CheckContext) => CheckResult | Promise<CheckResult>;
321
450
 
@@ -441,9 +570,10 @@ interface AuditTrace {
441
570
  /**
442
571
  * `ran` — the audit executed and returned.
443
572
  * `skipped` — no scanned page matched its `applicablePageTypes`.
573
+ * `gated` — the scan never obtained evidence the audit needs.
444
574
  * `error` — it threw, or its result was rejected by the schema.
445
575
  */
446
- outcome: 'ran' | 'skipped' | 'error';
576
+ outcome: 'ran' | 'skipped' | 'gated' | 'error';
447
577
  status: CheckStatus;
448
578
  score: number;
449
579
  weight: number;
@@ -457,7 +587,7 @@ interface AuditTrace {
457
587
  /** The structured evidence behind the verdict, as the report carries it. */
458
588
  details?: CheckResult['details'];
459
589
  }
460
- /** Which of the three outcomes a finished check represents. */
590
+ /** Which outcome a finished check represents. */
461
591
  declare function outcomeOf(check: CheckResult): AuditTrace['outcome'];
462
592
  /** Build a trace record from a finished check. */
463
593
  declare function traceFromCheck(check: CheckResult, durationMs: number): AuditTrace;
@@ -496,12 +626,26 @@ interface AuditPlan {
496
626
  }>;
497
627
  skipped: CheckResult[];
498
628
  }
629
+ /** How `planAudits` should treat audits the scan cannot feed. */
630
+ interface PlanOptions {
631
+ /**
632
+ * Skip audits whose `requires` the scan did not obtain.
633
+ *
634
+ * The orchestrator turns this on for every scan
635
+ * (`enforceEvidenceGate ?? true`), so an audit's `requires` decides what a
636
+ * blocked or client-rendered scan reports. It defaults to off in this option
637
+ * bag only so a caller planning audits directly gets the unfiltered set;
638
+ * passing `false` is the measurement escape hatch for comparing a gated run
639
+ * against an ungated one.
640
+ */
641
+ enforceEvidence?: boolean;
642
+ }
499
643
  /**
500
644
  * Split a scan config into the audits that will actually execute and the
501
- * page-type-skipped `na` stubs. Exported so the orchestrator can size the
645
+ * `na` stubs for those it will not. Exported so the orchestrator can size the
502
646
  * audits progress phase before running it.
503
647
  */
504
- declare function planAudits(ctx: CheckContext, config: ScanConfig): AuditPlan;
648
+ declare function planAudits(ctx: CheckContext, config: ScanConfig, options?: PlanOptions): AuditPlan;
505
649
  /**
506
650
  * Execute all audits registered in the config against the scan context.
507
651
  * Returns check results grouped into categories with weighted scores.
@@ -545,7 +689,8 @@ type ScanEvent = {
545
689
  } | {
546
690
  type: 'scan:done';
547
691
  durationMs: number;
548
- score: number;
692
+ /** Null when the scan obtained too little evidence to judge the site. */
693
+ score: number | null;
549
694
  fraction: number;
550
695
  elapsedMs: number;
551
696
  };
@@ -583,7 +728,7 @@ declare class ProgressTracker {
583
728
  /** A failed unit still counts as settled work so the phase can complete. */
584
729
  unitFail(label: string, error: string): void;
585
730
  phaseDone(): void;
586
- scanDone(score: number): void;
731
+ scanDone(score: number | null): void;
587
732
  }
588
733
 
589
734
  interface ScanOptions {
@@ -607,6 +752,48 @@ interface ScanOptions {
607
752
  * evidence it was drawn from; see {@link AuditTrace}.
608
753
  */
609
754
  onAuditTrace?: AuditTraceHandler;
755
+ /**
756
+ * Skip audits the scan could not feed, instead of running them blind.
757
+ *
758
+ * On by default. An audit whose required evidence the scan never obtained
759
+ * reports `na` tagged `skipped:no-evidence`, with the reason attached, and
760
+ * is never constructed. Set it to `false` to run every audit regardless —
761
+ * useful when measuring what the gate removes.
762
+ */
763
+ enforceEvidenceGate?: boolean;
764
+ /**
765
+ * undici dispatcher for every request this scan makes.
766
+ *
767
+ * Omitted, the scan uses the fetcher's shared agent, which opens as many
768
+ * connections per origin as the scan asks for. A caller scanning origins that
769
+ * did not invite it — the nightly corpus job — passes a bounded agent so the
770
+ * ~28 root-file requests the scan issues at once do not arrive at a stranger's
771
+ * WAF as a burst.
772
+ */
773
+ dispatcher?: Dispatcher;
774
+ /**
775
+ * How many requests this scan may have in flight at once.
776
+ *
777
+ * Omitted, the scan issues every request as it reaches the fetcher. A caller
778
+ * scanning origins that did not invite it sets this alongside `dispatcher`:
779
+ * the dispatcher bounds the sockets, and this bounds what is issued, so the
780
+ * requests the scan holds back wait outside the per-request deadline and
781
+ * outside `ttfbMs` instead of inside undici's queue. Without it a bounded
782
+ * scan reports its own queueing as the origin being slow or unreachable.
783
+ */
784
+ maxConcurrent?: number;
785
+ /**
786
+ * A `robots.txt` response the caller already has, used instead of fetching
787
+ * it again.
788
+ *
789
+ * For a caller that must read `robots.txt` before it decides to scan at all.
790
+ * The nightly corpus job is one: it asks permission first, and without this
791
+ * every site it visits gets two requests for the one file its owner watches.
792
+ * It must be the response to `<baseUrl>/robots.txt` fetched with this
793
+ * scanner's own user agent — anything else and the scan judges a file it was
794
+ * not served.
795
+ */
796
+ robotsTxt?: FetchResult;
610
797
  }
611
798
  declare function runScan(url: string, options?: ScanOptions): Promise<ScanReport>;
612
799
 
@@ -910,7 +1097,28 @@ declare function extractHeadings($: CheerioAPI): Array<{
910
1097
  level: number;
911
1098
  text: string;
912
1099
  }>;
1100
+ /**
1101
+ * The text of the page's main content region.
1102
+ *
1103
+ * Answers what the main content says, so page chrome — navigation, headers,
1104
+ * footers, cookie banners — stays out of it. Among several <main> elements the
1105
+ * one holding the most text wins: themes ship empty or fragmented <main>
1106
+ * wrappers, and the first one is often a short stub. Falls back to the whole
1107
+ * <body> only when no <main> holds any text.
1108
+ *
1109
+ * For shell detection — did the server render any readable text at all — use
1110
+ * `getRenderedText` instead.
1111
+ */
913
1112
  declare function getMainContentText($: CheerioAPI): string;
1113
+ /**
1114
+ * The text of the served HTML body, minus script/style/noscript/template.
1115
+ *
1116
+ * Answers whether the server rendered any readable text at all, so page chrome
1117
+ * counts: a shell that ships only a nav and a footer is exactly what this has
1118
+ * to see. Use `getMainContentText` when the question is what the main content
1119
+ * says rather than whether anything was served.
1120
+ */
1121
+ declare function getRenderedText($: CheerioAPI): string;
914
1122
  declare function getWordCount($: CheerioAPI): number;
915
1123
  declare function extractImages($: CheerioAPI): Array<{
916
1124
  src: string;
@@ -963,6 +1171,8 @@ declare const MAX_CONCURRENT_REQUESTS = 10;
963
1171
  declare const SCANNER_USER_AGENT = "AgentLighthouse/1.0 (+https://github.com/ForkPoint/agent-lighthouse)";
964
1172
  declare const TAG_SKIPPED_PAGE_TYPE = "skipped:page-type";
965
1173
  declare const TAG_SCAN_ERROR = "scan-error";
1174
+ /** An audit the scan could not feed: it needs evidence this scan never got. */
1175
+ declare const TAG_SKIPPED_NO_EVIDENCE = "skipped:no-evidence";
966
1176
  /** Display names for the 8 v2 categories. */
967
1177
  declare const CATEGORY_NAMES: Record<string, string>;
968
1178
  declare const READINESS_WEIGHTS: {
@@ -1113,6 +1323,7 @@ declare const EvidenceGradeSchema: z.ZodEnum<["A", "B", "C", "D"]>;
1113
1323
  declare const AuditTierSchema: z.ZodEnum<["scored", "informative", "experimental"]>;
1114
1324
  /** v2 audit identity: `category/slug`, stable across releases (spec §6). */
1115
1325
  declare const AUDIT_ID_PATTERN: RegExp;
1326
+ declare const EvidenceKeySchema: z.ZodEnum<["origin-reachable", "unblocked-fetches", "rendered-body", "sample-adequate"]>;
1116
1327
  declare const AuditMetaSchema: z.ZodObject<{
1117
1328
  id: z.ZodString;
1118
1329
  category: z.ZodString;
@@ -1158,6 +1369,7 @@ declare const AuditMetaSchema: z.ZodObject<{
1158
1369
  evidenceGrade: z.ZodEnum<["A", "B", "C", "D"]>;
1159
1370
  tier: z.ZodEnum<["scored", "informative", "experimental"]>;
1160
1371
  dossier: z.ZodString;
1372
+ requires: z.ZodOptional<z.ZodArray<z.ZodEnum<["origin-reachable", "unblocked-fetches", "rendered-body", "sample-adequate"]>, "many">>;
1161
1373
  }, "strip", z.ZodTypeAny, {
1162
1374
  category: string;
1163
1375
  title: string;
@@ -1183,6 +1395,7 @@ declare const AuditMetaSchema: z.ZodObject<{
1183
1395
  link: string;
1184
1396
  notice: string;
1185
1397
  } | undefined;
1398
+ requires?: ("origin-reachable" | "unblocked-fetches" | "rendered-body" | "sample-adequate")[] | undefined;
1186
1399
  }, {
1187
1400
  category: string;
1188
1401
  title: string;
@@ -1208,6 +1421,7 @@ declare const AuditMetaSchema: z.ZodObject<{
1208
1421
  link: string;
1209
1422
  notice: string;
1210
1423
  } | undefined;
1424
+ requires?: ("origin-reachable" | "unblocked-fetches" | "rendered-body" | "sample-adequate")[] | undefined;
1211
1425
  }>;
1212
1426
  declare const CheckResultSchema: z.ZodObject<{
1213
1427
  id: z.ZodString;
@@ -1376,4 +1590,4 @@ type AcpLinkType = (typeof ACP_LINK_TYPES)[number];
1376
1590
  */
1377
1591
  declare function resolvePolicyLinks(ctx: CheckContext): Map<AcpLinkType, string>;
1378
1592
 
1379
- export { ACP_LINK_TYPES, AI_CRAWLER_UAS, AUDIT_ID_PATTERN, type AcpLinkType, type AgentLighthouseConfig, Audit, type AuditGuidance, AuditGuidanceSchema, type AuditMeta, AuditMetaSchema, type AuditPlan, type AuditProgressEvent, type AuditRegistration, type AuditResult, AuditResultSchema, type AuditRunResult, type AuditTier, AuditTierSchema, type AuditTrace, type AuditTraceHandler, BASELINE_UA, type BlockClass, CATEGORY_IDS, CATEGORY_MASS, CATEGORY_NAMES, type CategoryConfig, type CategoryResult, type CheckContext, type CheckFn, type CheckPriority, CheckPrioritySchema, type CheckRecommendation, type CheckResult, CheckResultSchema, type CheckStatus, CheckStatusSchema, type CssRule, DEFAULT_SCAN_LIMIT, type DeprecationNotice, DeprecationNoticeSchema, type EvidenceGrade, EvidenceGradeSchema, type ExpectedKind, type FetchClass, type FetchOptions, type FetchResult, type FieldStatus, type FixEffort, FixEffortSchema, MAX_CONCURRENT_REQUESTS, MAX_PAGES_PER_SCAN, MAX_RESPONSE_BODY_BYTES, PAGE_TYPE_LABELS, PHASE_WEIGHTS, PRESETS, type PageContext, type PageCss, type PageJudgement, type PageOverride, type PageType, type PhaseId, type PresetName, type PresetOptions, type ProductFieldVerification, ProgressTracker, READINESS_WEIGHTS, REQUEST_TIMEOUT_MS, type ReadinessVitals, type RobotsFile, type RobotsGroup, type RobotsRule, SCANNER_USER_AGENT, SCAN_TIMEOUT_MS, SCORE_TIER_LABELS, type ScanConfig, type ScanEvent, type ScanOptions, type ScanReport, type ScoreDisplayMode, ScoreDisplayModeSchema, type ScoreTier, type SitemapEntry, type SitemapTree, TAG_SCAN_ERROR, TAG_SKIPPED_PAGE_TYPE, type UaProbe, type WafProtection, allJsonLdNodes, buildCategoryResult, calculateCategoryScore, calculateOverallScore, classifyFetch, classifyResponse, collectPageCss, collectSitemapEntries, createFetcher, defaultConfig, defineConfig, detectPageType, detectWafProtection, evidenceUrl, extractDomain, extractForms, extractHeadLinks, extractHeadings, extractImages, extractInternalLinks, extractJsonLd, extractMarkdownLinks, extractMetaTags, extractNavLinks, extractProductFieldVerification, extractScripts, extractStylesheetUrls, filterConfig, flattenJsonLd, formatTrace, getMainContentText, getPreset, getScoreTier, getTierColor, getTierLabel, getWordCount, groupsForBot, hasNamedGroup, isBlanketBlocked, isInformative, isPathAllowed, isPrivateIp, isRealFile, isSafeUrl, isW3CDateTime, joinUrl, judgePages, loadConfigFile, logger, matchesUserAgent, normalizeNewlines, normalizeUrl, outcomeOf, pagesOfType, parseCssRules, parseHtml, parseRobots, parseRobotsFile, planAudits, probeUaParity, resolvePolicyLinks, runAudits, runScan, sampleEntries, stripBom, topLevelJsonLd, traceFromCheck, weightForGrade };
1593
+ export { ACP_LINK_TYPES, AI_CRAWLER_UAS, AUDIT_ID_PATTERN, type AcpLinkType, type AgentLighthouseConfig, Audit, type AuditGuidance, AuditGuidanceSchema, type AuditMeta, AuditMetaSchema, type AuditPlan, type AuditProgressEvent, type AuditRegistration, type AuditResult, AuditResultSchema, type AuditRunResult, type AuditTier, AuditTierSchema, type AuditTrace, type AuditTraceHandler, BASELINE_UA, type BlockClass, CATEGORY_IDS, CATEGORY_MASS, CATEGORY_NAMES, type CategoryConfig, type CategoryResult, type CheckContext, type CheckFn, type CheckPriority, CheckPrioritySchema, type CheckRecommendation, type CheckResult, CheckResultSchema, type CheckStatus, CheckStatusSchema, type CssRule, DEFAULT_SCAN_LIMIT, type DeprecationNotice, DeprecationNoticeSchema, type EvidenceGrade, EvidenceGradeSchema, type EvidenceKey, EvidenceKeySchema, type ExpectedKind, type FetchClass, type FetchOptions, type FetchResult, type FetcherOptions, type FieldStatus, type FixEffort, FixEffortSchema, MAX_CONCURRENT_REQUESTS, MAX_PAGES_PER_SCAN, MAX_RESPONSE_BODY_BYTES, PAGE_TYPE_LABELS, PHASE_WEIGHTS, PRESETS, type PageContext, type PageCss, type PageJudgement, type PageOverride, type PageType, type PhaseId, type PresetName, type PresetOptions, type ProductFieldVerification, ProgressTracker, READINESS_WEIGHTS, REQUEST_TIMEOUT_MS, type ReadinessVitals, type RobotsFile, type RobotsGroup, type RobotsRule, SCANNER_USER_AGENT, SCAN_TIMEOUT_MS, SCORE_TIER_LABELS, type ScanConfig, type ScanEvent, type ScanEvidence, type ScanOptions, type ScanReport, type ScanValidity, type ScoreDisplayMode, ScoreDisplayModeSchema, type ScoreTier, type SitemapEntry, type SitemapTree, TAG_SCAN_ERROR, TAG_SKIPPED_NO_EVIDENCE, TAG_SKIPPED_PAGE_TYPE, type UaProbe, type WafProtection, allEvidenceMet, allJsonLdNodes, boundedDispatcher, buildCategoryResult, buildScanEvidence, calculateCategoryScore, calculateOverallScore, classifyFetch, classifyResponse, collectPageCss, collectSitemapEntries, createFetcher, defaultConfig, defineConfig, detectPageType, detectWafProtection, evidenceUrl, extractDomain, extractForms, extractHeadLinks, extractHeadings, extractImages, extractInternalLinks, extractJsonLd, extractMarkdownLinks, extractMetaTags, extractNavLinks, extractProductFieldVerification, extractScripts, extractStylesheetUrls, filterConfig, flattenJsonLd, formatTrace, getMainContentText, getPreset, getRenderedText, getScoreTier, getTierColor, getTierLabel, getWordCount, groupsForBot, hasNamedGroup, isBlanketBlocked, isInformative, isPathAllowed, isPrivateIp, isRealFile, isSafeUrl, isW3CDateTime, joinUrl, judgePages, loadConfigFile, logger, matchesUserAgent, normalizeNewlines, normalizeUrl, outcomeOf, pagesOfType, parseCssRules, parseHtml, parseRobots, parseRobotsFile, planAudits, probeUaParity, resolvePolicyLinks, runAudits, runScan, sampleEntries, stripBom, topLevelJsonLd, traceFromCheck, weightForGrade };