@webability/core 1.7.0 → 1.9.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.
@@ -1,257 +0,0 @@
1
- /**
2
- * @webability/core — type definitions
3
- */
4
- /**
5
- * Minimal page interface satisfied by both Playwright and Puppeteer.
6
- * No runtime wrapper needed — both libraries implement these methods natively.
7
- */
8
- interface PageLike {
9
- evaluate<R>(pageFunction: string | ((...args: any[]) => R | Promise<R>), ...args: any[]): Promise<R>;
10
- addScriptTag(options: {
11
- content: string;
12
- }): Promise<unknown>;
13
- url(): string;
14
- /**
15
- * Real keyboard event dispatch (Playwright & Puppeteer both expose this).
16
- * Optional so minimal PageLike shims still typecheck. The focusVisible
17
- * detector uses `keyboard.press('Tab')` to put the browser into keyboard
18
- * interaction modality before probing focus styles — otherwise
19
- * `:focus-visible` / keyboard-detection-class patterns (focus-visible
20
- * polyfill, `.user-is-tabbing` body class) never activate under a bare
21
- * programmatic `.focus()`, producing mass false positives.
22
- */
23
- keyboard?: {
24
- press(key: string): Promise<void>;
25
- };
26
- }
27
- /** All fix issue types the pipeline can detect and fix. */
28
- type FixIssueType = 'weak_button_name' | 'weak_link_name' | 'weak_heading' | 'weak_iframe_title' | 'weak_page_title' | 'weak_svg_name' | 'weak_skip_link' | 'missing_button_role' | 'missing_semantic_role' | 'missing_landmark' | 'heading_skip' | 'svg_missing_name' | 'aria_hidden_focusable' | 'adjacent_redundant_link' | 'contrast_insufficient' | 'non_text_contrast_insufficient' | 'target_too_small' | 'focus_not_visible' | 'empty_link' | 'unlabeled_button' | 'missing_label' | 'decorative_icon' | 'skip_link' | 'aria_live' | 'missing_alt' | 'identify_input_purpose' | 'redundant_role' | 'duplicate_id' | 'new_window_link' | 'missing_fieldset_legend' | 'missing_table_header' | 'missing_table_caption' | 'missing_table_scope' | 'external_link_warning' | 'download_link_warning' | 'broken_anchor' | 'placeholder_link' | 'missing_pattern_title' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'invalid_aria_role' | 'zoom_restriction' | 'missing_button_type' | 'link_consistency' | 'semantic_emphasis' | 'keyboard_trap' | 'focus_order_disruption' | 'missing_captions' | 'color_only_link' | 'label_in_name' | 'onfocus_context_change' | 'focus_obscured_by_sticky' | 'text_spacing_blocked' | 'nested_landmark' | 'combobox_name_missing' | 'landmark_duplicate_unlabeled' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'missing_h1' | 'multiple_h1' | 'empty_heading' | 'javascript_link' | 'broken_aria_reference' | 'main_content_hidden' | 'scrollable_not_focusable' | 'meaningful_sequence' | 'sensory_characteristic' | 'orientation_lock' | 'image_of_text' | 'reflow_overflow' | 'missing_audio_description' | 'missing_transcript' | 'character_key_shortcut' | 'on_input_change' | 'pointer_gesture' | 'pointer_cancellation' | 'motion_actuation' | 'error_prevention_missing' | 'insufficient_navigation' | 'inconsistent_navigation';
29
- /** WCAG criterion for each issue type. */
30
- declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
31
- /** A single accessibility issue detected on the page. */
32
- interface DetectedIssue {
33
- /** Unique CSS selector targeting the element. */
34
- selector: string;
35
- /** HTML/ARIA attribute to set (alt, aria-label, role, tabindex, etc.) */
36
- attribute: string;
37
- /** What the element currently has (empty string if missing). */
38
- currentValue: string;
39
- /** Classification of the issue. */
40
- issueType: FixIssueType;
41
- /** WCAG success criterion reference. */
42
- wcag: string;
43
- /** Contextual info for Claude: nearby heading, parent tag, href, page section. */
44
- context: string;
45
- /** true = needs Claude to generate the fix value; false = rule already knows it. */
46
- needsAI: boolean;
47
- /** When needsAI is false, this is the known fix value. */
48
- fixValue?: string;
49
- /** Optional: image src for alt text generation via vision. */
50
- imageSrc?: string;
51
- /** Optional: outer HTML snippet (first 300 chars) for Claude context. */
52
- outerHtml?: string;
53
- /**
54
- * Detector-side signal that the VERDICT is uncertain (e.g. contrast against
55
- * a gradient). The scanner never reports such a finding: it is resolved to a
56
- * verdict by an engine resolver or dropped. For "verdict certain, fix not
57
- * mechanical" use `manualFix`.
58
- */
59
- needsReview?: boolean;
60
- /**
61
- * The verdict is certain but the suggested fix cannot be applied
62
- * mechanically. Sets `fix.needsManualReview`; never changes the verdict.
63
- */
64
- manualFix?: boolean;
65
- /** Why this finding needs review — surfaced to MCP/SDK consumers. */
66
- reviewReason?: string;
67
- /**
68
- * Optional exact user-facing message. Use when the generic
69
- * attribute/currentValue template ("missing X", "X is \"Y\"") would state
70
- * something false or fabricated — e.g. a weak button name whose name comes
71
- * from text content has no missing aria-label to report (persona round 3,
72
- * P069: 'weak button name: missing aria-label' on a named control).
73
- * formatMessage prefers this verbatim when present.
74
- */
75
- message?: string;
76
- }
77
- /** Every fix detector must implement this interface. */
78
- interface FixDetector {
79
- name: string;
80
- detect(page: PageLike): Promise<DetectedIssue[]>;
81
- /** Per-page emission cap, when the detector has one. When a run returns
82
- * >= cap findings the results are TRUNCATED (DOM-order biased) and the
83
- * scan reports it via ScanResult.truncated — silent truncation reads as
84
- * "covered everything" when it didn't (loop iter8 / honesty infra). */
85
- cap?: number;
86
- }
87
- /**
88
- * Browser-runtime detector signature.
89
- *
90
- * Each detector exports a pure, zero-argument browser function that uses the
91
- * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
92
- * Node imports, no closures over module-scope variables — so it both:
93
- * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
94
- * stringifies and re-parses the function inside the browser context).
95
- * 2. Bundles directly into the widget via webpack and runs against the
96
- * live customer DOM.
97
- *
98
- * Single source of truth — same rules run in both runtimes.
99
- */
100
- type BrowserDetector = () => DetectedIssue[];
101
- interface SiteTheme {
102
- cssVars: Record<string, string>;
103
- topBgColors: Array<{
104
- color: string;
105
- count: number;
106
- }>;
107
- topTextColors: Array<{
108
- color: string;
109
- count: number;
110
- }>;
111
- }
112
- type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
113
- type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
114
- type WcagLevel = 'A' | 'AA' | 'AAA';
115
- /**
116
- * Confidence tier for a scan finding. Mirrors axe-core's three-tier output —
117
- * `high` is a confirmed violation safe to act on; `needs_review` is ambiguous
118
- * (e.g. text on a gradient background, mid-animation opacity, marketing imagery
119
- * that may legitimately contain text). Consumers should never auto-apply fixes
120
- * to `needs_review` findings — they belong in the `incomplete[]` bucket and
121
- * require human judgment.
122
- */
123
- type IssueConfidence = 'high' | 'needs_review';
124
- interface ScanIssue {
125
- id: string;
126
- source: IssueSource;
127
- type: FixIssueType | string;
128
- wcag: string;
129
- level: WcagLevel;
130
- impact: IssueImpact;
131
- message: string;
132
- selector: string;
133
- html?: string;
134
- /** When set, this issue is ambiguous and lives in `ScanResult.incomplete[]`. */
135
- confidence?: IssueConfidence;
136
- /** Why the classifier demoted this issue. Surfaced to MCP/SDK consumers. */
137
- reviewReason?: string;
138
- fix?: {
139
- attribute: string;
140
- currentValue: string;
141
- suggestedValue?: string;
142
- needsManualReview: boolean;
143
- };
144
- }
145
- /**
146
- * Signal that the scan never reached the intended page — the target served a
147
- * bot-challenge / interstitial (Cloudflare "Just a moment…", "Access Denied",
148
- * a CAPTCHA wall) or the main document returned an HTTP error status. When
149
- * present, `issues`/`incomplete` are empty and MUST NOT be read as "the page is
150
- * clean": nothing real was scanned. Lets a caller distinguish "scanned and
151
- * clean" from "was never actually scanned".
152
- */
153
- interface BlockedInfo {
154
- /** Human-readable explanation of why the page could not be scanned. */
155
- reason: string;
156
- /** Challenge vendor when identifiable (e.g. 'Cloudflare', 'PerimeterX/HUMAN'). */
157
- vendor?: string;
158
- /** HTTP status of the main document response, when known. */
159
- status?: number;
160
- /** The specific signal that matched (page title, marker selector, or status). */
161
- signal?: string;
162
- }
163
- interface ScanResult {
164
- url: string;
165
- timestamp: string;
166
- /**
167
- * Set when the target served a bot-challenge/interstitial or an HTTP error
168
- * instead of the real page. When present, treat `issues`/`incomplete` as
169
- * "not scanned" (they are empty), NOT as "no problems found".
170
- */
171
- blocked?: BlockedInfo;
172
- /** High-confidence violations. Safe to surface as bugs. */
173
- issues: ScanIssue[];
174
- /** Detectors that hit their per-page cap this run — their counts are lower
175
- * bounds, not a census. Additive field; absent = nothing truncated. */
176
- truncated?: Array<{
177
- detector: string;
178
- cap: number;
179
- }>;
180
- /**
181
- * Non-fatal engine subsystem failures that silently disable a category of
182
- * checks. Consumers MUST surface these to the user — a result with
183
- * `engineWarnings` is NOT a full scan. Currently: `'color:kernel-injection-failed'`
184
- * disables all contrast checks for the page. Absent = all subsystems healthy.
185
- */
186
- engineWarnings?: string[];
187
- /** Engine (core package) version that produced this result — provenance */
188
- engine?: string;
189
- /**
190
- * @deprecated Always `[]`. The scanner judges uncertain findings itself:
191
- * each becomes a verdict in `issues` or is dropped. The field stays only so
192
- * existing consumers that read it keep compiling.
193
- */
194
- incomplete: ScanIssue[];
195
- summary: {
196
- total: number;
197
- critical: number;
198
- serious: number;
199
- moderate: number;
200
- minor: number;
201
- /** @deprecated Always 0 — there is no incomplete bucket. */
202
- incomplete: number;
203
- };
204
- axeResults?: unknown;
205
- }
206
- type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
207
- declare const VIEWPORT_SIZES: Record<ViewportPreset, {
208
- width: number;
209
- height: number;
210
- }>;
211
- interface ScanOptions {
212
- wcagTags?: string[];
213
- /**
214
- * Highest WCAG conformance level to report. Default 'AA': findings whose
215
- * criteria are all Level AAA are not reported anywhere (issues, incomplete,
216
- * summary). 'AAA' opts in (and adds axe's wcag2aaa rules); 'A' keeps Level A only.
217
- */
218
- level?: WcagLevel;
219
- includeAxe?: boolean;
220
- deep?: boolean;
221
- deepApiUrl?: string;
222
- deepApiKey?: string;
223
- dismissModals?: boolean;
224
- viewport?: ViewportPreset | {
225
- width: number;
226
- height: number;
227
- };
228
- browser?: {
229
- headless?: boolean;
230
- timeout?: number;
231
- };
232
- review?: boolean;
233
- /** Limit scan to elements within this CSS selector. */
234
- rootSelector?: string;
235
- /** Include annotated screenshots of violations in results. */
236
- enableScreenshots?: boolean;
237
- /** Scan content inside iframes. Default: false. */
238
- includeIframes?: boolean;
239
- /**
240
- * Wait for entrance animations to finish before scanning (prevents false
241
- * contrast failures on mid-fade text). `false` disables, a number sets the
242
- * wait cap in ms. Default: enabled with a 4000ms cap.
243
- */
244
- settle?: boolean | number;
245
- /**
246
- * HTTP status of the main-document response, for the Page-object call shape
247
- * where the CALLER navigated (e.g. mcp's flow_scan does its own `page.goto`).
248
- * The URL-string path captures this itself; on the Page path core cannot see
249
- * the response, so a plain 4xx/5xx error page with no challenge DOM markers
250
- * would otherwise be scanned as a clean page. Pass the caller's
251
- * `response.status()` here so bot-challenge/error-page detection works
252
- * uniformly across both entrypoints. Ignored on the URL-string path.
253
- */
254
- mainStatus?: number;
255
- }
256
-
257
- export { type BrowserDetector as B, type DetectedIssue as D, type FixDetector as F, type IssueImpact as I, type PageLike as P, type ScanResult as S, VIEWPORT_SIZES as V, type WcagLevel as W, type ScanOptions as a, type SiteTheme as b, type ScanIssue as c, type FixIssueType as d, type IssueSource as e, type ViewportPreset as f, WCAG_BY_ISSUE as g };
@@ -1,257 +0,0 @@
1
- /**
2
- * @webability/core — type definitions
3
- */
4
- /**
5
- * Minimal page interface satisfied by both Playwright and Puppeteer.
6
- * No runtime wrapper needed — both libraries implement these methods natively.
7
- */
8
- interface PageLike {
9
- evaluate<R>(pageFunction: string | ((...args: any[]) => R | Promise<R>), ...args: any[]): Promise<R>;
10
- addScriptTag(options: {
11
- content: string;
12
- }): Promise<unknown>;
13
- url(): string;
14
- /**
15
- * Real keyboard event dispatch (Playwright & Puppeteer both expose this).
16
- * Optional so minimal PageLike shims still typecheck. The focusVisible
17
- * detector uses `keyboard.press('Tab')` to put the browser into keyboard
18
- * interaction modality before probing focus styles — otherwise
19
- * `:focus-visible` / keyboard-detection-class patterns (focus-visible
20
- * polyfill, `.user-is-tabbing` body class) never activate under a bare
21
- * programmatic `.focus()`, producing mass false positives.
22
- */
23
- keyboard?: {
24
- press(key: string): Promise<void>;
25
- };
26
- }
27
- /** All fix issue types the pipeline can detect and fix. */
28
- type FixIssueType = 'weak_button_name' | 'weak_link_name' | 'weak_heading' | 'weak_iframe_title' | 'weak_page_title' | 'weak_svg_name' | 'weak_skip_link' | 'missing_button_role' | 'missing_semantic_role' | 'missing_landmark' | 'heading_skip' | 'svg_missing_name' | 'aria_hidden_focusable' | 'adjacent_redundant_link' | 'contrast_insufficient' | 'non_text_contrast_insufficient' | 'target_too_small' | 'focus_not_visible' | 'empty_link' | 'unlabeled_button' | 'missing_label' | 'decorative_icon' | 'skip_link' | 'aria_live' | 'missing_alt' | 'identify_input_purpose' | 'redundant_role' | 'duplicate_id' | 'new_window_link' | 'missing_fieldset_legend' | 'missing_table_header' | 'missing_table_caption' | 'missing_table_scope' | 'external_link_warning' | 'download_link_warning' | 'broken_anchor' | 'placeholder_link' | 'missing_pattern_title' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'invalid_aria_role' | 'zoom_restriction' | 'missing_button_type' | 'link_consistency' | 'semantic_emphasis' | 'keyboard_trap' | 'focus_order_disruption' | 'missing_captions' | 'color_only_link' | 'label_in_name' | 'onfocus_context_change' | 'focus_obscured_by_sticky' | 'text_spacing_blocked' | 'nested_landmark' | 'combobox_name_missing' | 'landmark_duplicate_unlabeled' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'missing_h1' | 'multiple_h1' | 'empty_heading' | 'javascript_link' | 'broken_aria_reference' | 'main_content_hidden' | 'scrollable_not_focusable' | 'meaningful_sequence' | 'sensory_characteristic' | 'orientation_lock' | 'image_of_text' | 'reflow_overflow' | 'missing_audio_description' | 'missing_transcript' | 'character_key_shortcut' | 'on_input_change' | 'pointer_gesture' | 'pointer_cancellation' | 'motion_actuation' | 'error_prevention_missing' | 'insufficient_navigation' | 'inconsistent_navigation';
29
- /** WCAG criterion for each issue type. */
30
- declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
31
- /** A single accessibility issue detected on the page. */
32
- interface DetectedIssue {
33
- /** Unique CSS selector targeting the element. */
34
- selector: string;
35
- /** HTML/ARIA attribute to set (alt, aria-label, role, tabindex, etc.) */
36
- attribute: string;
37
- /** What the element currently has (empty string if missing). */
38
- currentValue: string;
39
- /** Classification of the issue. */
40
- issueType: FixIssueType;
41
- /** WCAG success criterion reference. */
42
- wcag: string;
43
- /** Contextual info for Claude: nearby heading, parent tag, href, page section. */
44
- context: string;
45
- /** true = needs Claude to generate the fix value; false = rule already knows it. */
46
- needsAI: boolean;
47
- /** When needsAI is false, this is the known fix value. */
48
- fixValue?: string;
49
- /** Optional: image src for alt text generation via vision. */
50
- imageSrc?: string;
51
- /** Optional: outer HTML snippet (first 300 chars) for Claude context. */
52
- outerHtml?: string;
53
- /**
54
- * Detector-side signal that the VERDICT is uncertain (e.g. contrast against
55
- * a gradient). The scanner never reports such a finding: it is resolved to a
56
- * verdict by an engine resolver or dropped. For "verdict certain, fix not
57
- * mechanical" use `manualFix`.
58
- */
59
- needsReview?: boolean;
60
- /**
61
- * The verdict is certain but the suggested fix cannot be applied
62
- * mechanically. Sets `fix.needsManualReview`; never changes the verdict.
63
- */
64
- manualFix?: boolean;
65
- /** Why this finding needs review — surfaced to MCP/SDK consumers. */
66
- reviewReason?: string;
67
- /**
68
- * Optional exact user-facing message. Use when the generic
69
- * attribute/currentValue template ("missing X", "X is \"Y\"") would state
70
- * something false or fabricated — e.g. a weak button name whose name comes
71
- * from text content has no missing aria-label to report (persona round 3,
72
- * P069: 'weak button name: missing aria-label' on a named control).
73
- * formatMessage prefers this verbatim when present.
74
- */
75
- message?: string;
76
- }
77
- /** Every fix detector must implement this interface. */
78
- interface FixDetector {
79
- name: string;
80
- detect(page: PageLike): Promise<DetectedIssue[]>;
81
- /** Per-page emission cap, when the detector has one. When a run returns
82
- * >= cap findings the results are TRUNCATED (DOM-order biased) and the
83
- * scan reports it via ScanResult.truncated — silent truncation reads as
84
- * "covered everything" when it didn't (loop iter8 / honesty infra). */
85
- cap?: number;
86
- }
87
- /**
88
- * Browser-runtime detector signature.
89
- *
90
- * Each detector exports a pure, zero-argument browser function that uses the
91
- * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
92
- * Node imports, no closures over module-scope variables — so it both:
93
- * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
94
- * stringifies and re-parses the function inside the browser context).
95
- * 2. Bundles directly into the widget via webpack and runs against the
96
- * live customer DOM.
97
- *
98
- * Single source of truth — same rules run in both runtimes.
99
- */
100
- type BrowserDetector = () => DetectedIssue[];
101
- interface SiteTheme {
102
- cssVars: Record<string, string>;
103
- topBgColors: Array<{
104
- color: string;
105
- count: number;
106
- }>;
107
- topTextColors: Array<{
108
- color: string;
109
- count: number;
110
- }>;
111
- }
112
- type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
113
- type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
114
- type WcagLevel = 'A' | 'AA' | 'AAA';
115
- /**
116
- * Confidence tier for a scan finding. Mirrors axe-core's three-tier output —
117
- * `high` is a confirmed violation safe to act on; `needs_review` is ambiguous
118
- * (e.g. text on a gradient background, mid-animation opacity, marketing imagery
119
- * that may legitimately contain text). Consumers should never auto-apply fixes
120
- * to `needs_review` findings — they belong in the `incomplete[]` bucket and
121
- * require human judgment.
122
- */
123
- type IssueConfidence = 'high' | 'needs_review';
124
- interface ScanIssue {
125
- id: string;
126
- source: IssueSource;
127
- type: FixIssueType | string;
128
- wcag: string;
129
- level: WcagLevel;
130
- impact: IssueImpact;
131
- message: string;
132
- selector: string;
133
- html?: string;
134
- /** When set, this issue is ambiguous and lives in `ScanResult.incomplete[]`. */
135
- confidence?: IssueConfidence;
136
- /** Why the classifier demoted this issue. Surfaced to MCP/SDK consumers. */
137
- reviewReason?: string;
138
- fix?: {
139
- attribute: string;
140
- currentValue: string;
141
- suggestedValue?: string;
142
- needsManualReview: boolean;
143
- };
144
- }
145
- /**
146
- * Signal that the scan never reached the intended page — the target served a
147
- * bot-challenge / interstitial (Cloudflare "Just a moment…", "Access Denied",
148
- * a CAPTCHA wall) or the main document returned an HTTP error status. When
149
- * present, `issues`/`incomplete` are empty and MUST NOT be read as "the page is
150
- * clean": nothing real was scanned. Lets a caller distinguish "scanned and
151
- * clean" from "was never actually scanned".
152
- */
153
- interface BlockedInfo {
154
- /** Human-readable explanation of why the page could not be scanned. */
155
- reason: string;
156
- /** Challenge vendor when identifiable (e.g. 'Cloudflare', 'PerimeterX/HUMAN'). */
157
- vendor?: string;
158
- /** HTTP status of the main document response, when known. */
159
- status?: number;
160
- /** The specific signal that matched (page title, marker selector, or status). */
161
- signal?: string;
162
- }
163
- interface ScanResult {
164
- url: string;
165
- timestamp: string;
166
- /**
167
- * Set when the target served a bot-challenge/interstitial or an HTTP error
168
- * instead of the real page. When present, treat `issues`/`incomplete` as
169
- * "not scanned" (they are empty), NOT as "no problems found".
170
- */
171
- blocked?: BlockedInfo;
172
- /** High-confidence violations. Safe to surface as bugs. */
173
- issues: ScanIssue[];
174
- /** Detectors that hit their per-page cap this run — their counts are lower
175
- * bounds, not a census. Additive field; absent = nothing truncated. */
176
- truncated?: Array<{
177
- detector: string;
178
- cap: number;
179
- }>;
180
- /**
181
- * Non-fatal engine subsystem failures that silently disable a category of
182
- * checks. Consumers MUST surface these to the user — a result with
183
- * `engineWarnings` is NOT a full scan. Currently: `'color:kernel-injection-failed'`
184
- * disables all contrast checks for the page. Absent = all subsystems healthy.
185
- */
186
- engineWarnings?: string[];
187
- /** Engine (core package) version that produced this result — provenance */
188
- engine?: string;
189
- /**
190
- * @deprecated Always `[]`. The scanner judges uncertain findings itself:
191
- * each becomes a verdict in `issues` or is dropped. The field stays only so
192
- * existing consumers that read it keep compiling.
193
- */
194
- incomplete: ScanIssue[];
195
- summary: {
196
- total: number;
197
- critical: number;
198
- serious: number;
199
- moderate: number;
200
- minor: number;
201
- /** @deprecated Always 0 — there is no incomplete bucket. */
202
- incomplete: number;
203
- };
204
- axeResults?: unknown;
205
- }
206
- type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
207
- declare const VIEWPORT_SIZES: Record<ViewportPreset, {
208
- width: number;
209
- height: number;
210
- }>;
211
- interface ScanOptions {
212
- wcagTags?: string[];
213
- /**
214
- * Highest WCAG conformance level to report. Default 'AA': findings whose
215
- * criteria are all Level AAA are not reported anywhere (issues, incomplete,
216
- * summary). 'AAA' opts in (and adds axe's wcag2aaa rules); 'A' keeps Level A only.
217
- */
218
- level?: WcagLevel;
219
- includeAxe?: boolean;
220
- deep?: boolean;
221
- deepApiUrl?: string;
222
- deepApiKey?: string;
223
- dismissModals?: boolean;
224
- viewport?: ViewportPreset | {
225
- width: number;
226
- height: number;
227
- };
228
- browser?: {
229
- headless?: boolean;
230
- timeout?: number;
231
- };
232
- review?: boolean;
233
- /** Limit scan to elements within this CSS selector. */
234
- rootSelector?: string;
235
- /** Include annotated screenshots of violations in results. */
236
- enableScreenshots?: boolean;
237
- /** Scan content inside iframes. Default: false. */
238
- includeIframes?: boolean;
239
- /**
240
- * Wait for entrance animations to finish before scanning (prevents false
241
- * contrast failures on mid-fade text). `false` disables, a number sets the
242
- * wait cap in ms. Default: enabled with a 4000ms cap.
243
- */
244
- settle?: boolean | number;
245
- /**
246
- * HTTP status of the main-document response, for the Page-object call shape
247
- * where the CALLER navigated (e.g. mcp's flow_scan does its own `page.goto`).
248
- * The URL-string path captures this itself; on the Page path core cannot see
249
- * the response, so a plain 4xx/5xx error page with no challenge DOM markers
250
- * would otherwise be scanned as a clean page. Pass the caller's
251
- * `response.status()` here so bot-challenge/error-page detection works
252
- * uniformly across both entrypoints. Ignored on the URL-string path.
253
- */
254
- mainStatus?: number;
255
- }
256
-
257
- export { type BrowserDetector as B, type DetectedIssue as D, type FixDetector as F, type IssueImpact as I, type PageLike as P, type ScanResult as S, VIEWPORT_SIZES as V, type WcagLevel as W, type ScanOptions as a, type SiteTheme as b, type ScanIssue as c, type FixIssueType as d, type IssueSource as e, type ViewportPreset as f, WCAG_BY_ISSUE as g };