@webability/core 1.2.1 → 1.3.1

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.cts CHANGED
@@ -1,238 +1,5 @@
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_aria_required' | '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 finding is ambiguous and should land in
55
- * `ScanResult.incomplete[]` (e.g. contrast against a gradient). The scanner
56
- * propagates this to ScanIssue.confidence so the confidence classifier
57
- * doesn't have to reverse-engineer the situation from HTML strings.
58
- */
59
- needsReview?: boolean;
60
- /** Why this finding needs review — surfaced to MCP/SDK consumers. */
61
- reviewReason?: string;
62
- }
63
- /** Every fix detector must implement this interface. */
64
- interface FixDetector {
65
- name: string;
66
- detect(page: PageLike): Promise<DetectedIssue[]>;
67
- /** Per-page emission cap, when the detector has one. When a run returns
68
- * >= cap findings the results are TRUNCATED (DOM-order biased) and the
69
- * scan reports it via ScanResult.truncated — silent truncation reads as
70
- * "covered everything" when it didn't (loop iter8 / honesty infra). */
71
- cap?: number;
72
- }
73
- /**
74
- * Browser-runtime detector signature.
75
- *
76
- * Each detector exports a pure, zero-argument browser function that uses the
77
- * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
78
- * Node imports, no closures over module-scope variables — so it both:
79
- * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
80
- * stringifies and re-parses the function inside the browser context).
81
- * 2. Bundles directly into the widget via webpack and runs against the
82
- * live customer DOM.
83
- *
84
- * Single source of truth — same rules run in both runtimes.
85
- */
86
- type BrowserDetector = () => DetectedIssue[];
87
- interface SiteTheme {
88
- cssVars: Record<string, string>;
89
- topBgColors: Array<{
90
- color: string;
91
- count: number;
92
- }>;
93
- topTextColors: Array<{
94
- color: string;
95
- count: number;
96
- }>;
97
- }
98
- type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
99
- type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
100
- type WcagLevel = 'A' | 'AA' | 'AAA';
101
- /**
102
- * Confidence tier for a scan finding. Mirrors axe-core's three-tier output —
103
- * `high` is a confirmed violation safe to act on; `needs_review` is ambiguous
104
- * (e.g. text on a gradient background, mid-animation opacity, marketing imagery
105
- * that may legitimately contain text). Consumers should never auto-apply fixes
106
- * to `needs_review` findings — they belong in the `incomplete[]` bucket and
107
- * require human judgment.
108
- */
109
- type IssueConfidence = 'high' | 'needs_review';
110
- interface ScanIssue {
111
- id: string;
112
- source: IssueSource;
113
- type: FixIssueType | string;
114
- wcag: string;
115
- level: WcagLevel;
116
- impact: IssueImpact;
117
- message: string;
118
- selector: string;
119
- html?: string;
120
- /** When set, this issue is ambiguous and lives in `ScanResult.incomplete[]`. */
121
- confidence?: IssueConfidence;
122
- /** Why the classifier demoted this issue. Surfaced to MCP/SDK consumers. */
123
- reviewReason?: string;
124
- fix?: {
125
- attribute: string;
126
- currentValue: string;
127
- suggestedValue?: string;
128
- needsManualReview: boolean;
129
- };
130
- }
131
- /**
132
- * Signal that the scan never reached the intended page — the target served a
133
- * bot-challenge / interstitial (Cloudflare "Just a moment…", "Access Denied",
134
- * a CAPTCHA wall) or the main document returned an HTTP error status. When
135
- * present, `issues`/`incomplete` are empty and MUST NOT be read as "the page is
136
- * clean": nothing real was scanned. Lets a caller distinguish "scanned and
137
- * clean" from "was never actually scanned".
138
- */
139
- interface BlockedInfo {
140
- /** Human-readable explanation of why the page could not be scanned. */
141
- reason: string;
142
- /** Challenge vendor when identifiable (e.g. 'Cloudflare', 'PerimeterX/HUMAN'). */
143
- vendor?: string;
144
- /** HTTP status of the main document response, when known. */
145
- status?: number;
146
- /** The specific signal that matched (page title, marker selector, or status). */
147
- signal?: string;
148
- }
149
- interface ScanResult {
150
- url: string;
151
- timestamp: string;
152
- /**
153
- * Set when the target served a bot-challenge/interstitial or an HTTP error
154
- * instead of the real page. When present, treat `issues`/`incomplete` as
155
- * "not scanned" (they are empty), NOT as "no problems found".
156
- */
157
- blocked?: BlockedInfo;
158
- /** High-confidence violations. Safe to surface as bugs. */
159
- issues: ScanIssue[];
160
- /** Detectors that hit their per-page cap this run — their counts are lower
161
- * bounds, not a census. Additive field; absent = nothing truncated. */
162
- truncated?: Array<{
163
- detector: string;
164
- cap: number;
165
- }>;
166
- /**
167
- * Non-fatal engine subsystem failures that silently disable a category of
168
- * checks. Consumers MUST surface these to the user — a result with
169
- * `engineWarnings` is NOT a full scan. Currently: `'color:kernel-injection-failed'`
170
- * disables all contrast checks for the page. Absent = all subsystems healthy.
171
- */
172
- engineWarnings?: string[];
173
- /** Engine (core package) version that produced this result — provenance */
174
- engine?: string;
175
- /**
176
- * Findings the classifier or axe-core flagged as ambiguous. Show to the user
177
- * for review — never auto-fix. Empty array (not omitted) when no incompletes,
178
- * so consumers can rely on the field.
179
- */
180
- incomplete: ScanIssue[];
181
- summary: {
182
- total: number;
183
- critical: number;
184
- serious: number;
185
- moderate: number;
186
- minor: number;
187
- /** Count of items in `incomplete[]`. */
188
- incomplete: number;
189
- };
190
- axeResults?: unknown;
191
- }
192
- type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
193
- declare const VIEWPORT_SIZES: Record<ViewportPreset, {
194
- width: number;
195
- height: number;
196
- }>;
197
- interface ScanOptions {
198
- wcagTags?: string[];
199
- includeAxe?: boolean;
200
- deep?: boolean;
201
- deepApiUrl?: string;
202
- deepApiKey?: string;
203
- dismissModals?: boolean;
204
- viewport?: ViewportPreset | {
205
- width: number;
206
- height: number;
207
- };
208
- browser?: {
209
- headless?: boolean;
210
- timeout?: number;
211
- };
212
- review?: boolean;
213
- /** Limit scan to elements within this CSS selector. */
214
- rootSelector?: string;
215
- /** Include annotated screenshots of violations in results. */
216
- enableScreenshots?: boolean;
217
- /** Scan content inside iframes. Default: false. */
218
- includeIframes?: boolean;
219
- /**
220
- * Wait for entrance animations to finish before scanning (prevents false
221
- * contrast failures on mid-fade text). `false` disables, a number sets the
222
- * wait cap in ms. Default: enabled with a 4000ms cap.
223
- */
224
- settle?: boolean | number;
225
- /**
226
- * HTTP status of the main-document response, for the Page-object call shape
227
- * where the CALLER navigated (e.g. mcp's flow_scan does its own `page.goto`).
228
- * The URL-string path captures this itself; on the Page path core cannot see
229
- * the response, so a plain 4xx/5xx error page with no challenge DOM markers
230
- * would otherwise be scanned as a clean page. Pass the caller's
231
- * `response.status()` here so bot-challenge/error-page detection works
232
- * uniformly across both entrypoints. Ignored on the URL-string path.
233
- */
234
- mainStatus?: number;
235
- }
1
+ import { P as PageLike, a as ScanOptions, S as ScanResult, b as SiteTheme, D as DetectedIssue, c as ScanIssue } from './types-10iqML3X.cjs';
2
+ export { B as BrowserDetector, F as FixDetector, d as FixIssueType, I as IssueImpact, e as IssueSource, V as VIEWPORT_SIZES, f as ViewportPreset, W as WCAG_BY_ISSUE, g as WcagLevel } from './types-10iqML3X.cjs';
236
3
 
237
4
  declare function scan(target: string | PageLike, options?: ScanOptions): Promise<ScanResult>;
238
5
 
@@ -246,7 +13,17 @@ type TruncationEntry = {
246
13
  * injection blocked by CSP) that silently disable a category of checks.
247
14
  * Both arrays are per-invocation (caller-owned), never module state.
248
15
  */
249
- declare function runDetectors(page: PageLike, outTruncated?: TruncationEntry[], outWarnings?: string[]): Promise<DetectedIssue[]>;
16
+ interface RunDetectorsOptions {
17
+ /**
18
+ * Skip the two serial FOCUS-MUTATING detectors (focusVisible moves real
19
+ * focus element-by-element; focusObscured scrolls anchor targets). Set by
20
+ * DOM-mode consumers scanning a page the user is LOOKING AT — stealing
21
+ * focus and scrolling their live tab is not acceptable there, and without
22
+ * page.keyboard those checks mostly land in needs-review anyway.
23
+ */
24
+ skipFocusMutating?: boolean;
25
+ }
26
+ declare function runDetectors(page: PageLike, outTruncated?: TruncationEntry[], outWarnings?: string[], options?: RunDetectorsOptions): Promise<DetectedIssue[]>;
250
27
  declare function extractSiteTheme(page: PageLike): Promise<SiteTheme>;
251
28
 
252
29
  interface AxeViolation {
@@ -348,4 +125,4 @@ interface DevSuggestion {
348
125
  }
349
126
  declare function generateDevSuggestion(issue: ScanIssue, framework: Framework, elementClasses?: string, fgColor?: string, bgColor?: string): DevSuggestion;
350
127
 
351
- export { type BrowserDetector, type ContrastSuggestion, type DetectedIssue, type DevSuggestion, type FixDetector, type FixIssueType, type Framework, type FrameworkInfo, type IssueImpact, type IssueSource, type PageLike, type ScanIssue, type ScanOptions, type ScanResult, type SiteTheme, type TailwindSuggestion, VIEWPORT_SIZES, type ViewportPreset, WCAG_BY_ISSUE, type WcagLevel, detectFramework, dismissModals, extractSiteTheme, generateDevSuggestion, getContrastRatio, runAxe, runDetectors, scan, solveContrast, suggestTailwindFix, toHtmlReport };
128
+ export { type ContrastSuggestion, DetectedIssue, type DevSuggestion, type Framework, type FrameworkInfo, PageLike, ScanIssue, ScanOptions, ScanResult, SiteTheme, type TailwindSuggestion, detectFramework, dismissModals, extractSiteTheme, generateDevSuggestion, getContrastRatio, runAxe, runDetectors, scan, solveContrast, suggestTailwindFix, toHtmlReport };
package/dist/index.d.ts CHANGED
@@ -1,238 +1,5 @@
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_aria_required' | '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 finding is ambiguous and should land in
55
- * `ScanResult.incomplete[]` (e.g. contrast against a gradient). The scanner
56
- * propagates this to ScanIssue.confidence so the confidence classifier
57
- * doesn't have to reverse-engineer the situation from HTML strings.
58
- */
59
- needsReview?: boolean;
60
- /** Why this finding needs review — surfaced to MCP/SDK consumers. */
61
- reviewReason?: string;
62
- }
63
- /** Every fix detector must implement this interface. */
64
- interface FixDetector {
65
- name: string;
66
- detect(page: PageLike): Promise<DetectedIssue[]>;
67
- /** Per-page emission cap, when the detector has one. When a run returns
68
- * >= cap findings the results are TRUNCATED (DOM-order biased) and the
69
- * scan reports it via ScanResult.truncated — silent truncation reads as
70
- * "covered everything" when it didn't (loop iter8 / honesty infra). */
71
- cap?: number;
72
- }
73
- /**
74
- * Browser-runtime detector signature.
75
- *
76
- * Each detector exports a pure, zero-argument browser function that uses the
77
- * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
78
- * Node imports, no closures over module-scope variables — so it both:
79
- * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
80
- * stringifies and re-parses the function inside the browser context).
81
- * 2. Bundles directly into the widget via webpack and runs against the
82
- * live customer DOM.
83
- *
84
- * Single source of truth — same rules run in both runtimes.
85
- */
86
- type BrowserDetector = () => DetectedIssue[];
87
- interface SiteTheme {
88
- cssVars: Record<string, string>;
89
- topBgColors: Array<{
90
- color: string;
91
- count: number;
92
- }>;
93
- topTextColors: Array<{
94
- color: string;
95
- count: number;
96
- }>;
97
- }
98
- type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
99
- type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
100
- type WcagLevel = 'A' | 'AA' | 'AAA';
101
- /**
102
- * Confidence tier for a scan finding. Mirrors axe-core's three-tier output —
103
- * `high` is a confirmed violation safe to act on; `needs_review` is ambiguous
104
- * (e.g. text on a gradient background, mid-animation opacity, marketing imagery
105
- * that may legitimately contain text). Consumers should never auto-apply fixes
106
- * to `needs_review` findings — they belong in the `incomplete[]` bucket and
107
- * require human judgment.
108
- */
109
- type IssueConfidence = 'high' | 'needs_review';
110
- interface ScanIssue {
111
- id: string;
112
- source: IssueSource;
113
- type: FixIssueType | string;
114
- wcag: string;
115
- level: WcagLevel;
116
- impact: IssueImpact;
117
- message: string;
118
- selector: string;
119
- html?: string;
120
- /** When set, this issue is ambiguous and lives in `ScanResult.incomplete[]`. */
121
- confidence?: IssueConfidence;
122
- /** Why the classifier demoted this issue. Surfaced to MCP/SDK consumers. */
123
- reviewReason?: string;
124
- fix?: {
125
- attribute: string;
126
- currentValue: string;
127
- suggestedValue?: string;
128
- needsManualReview: boolean;
129
- };
130
- }
131
- /**
132
- * Signal that the scan never reached the intended page — the target served a
133
- * bot-challenge / interstitial (Cloudflare "Just a moment…", "Access Denied",
134
- * a CAPTCHA wall) or the main document returned an HTTP error status. When
135
- * present, `issues`/`incomplete` are empty and MUST NOT be read as "the page is
136
- * clean": nothing real was scanned. Lets a caller distinguish "scanned and
137
- * clean" from "was never actually scanned".
138
- */
139
- interface BlockedInfo {
140
- /** Human-readable explanation of why the page could not be scanned. */
141
- reason: string;
142
- /** Challenge vendor when identifiable (e.g. 'Cloudflare', 'PerimeterX/HUMAN'). */
143
- vendor?: string;
144
- /** HTTP status of the main document response, when known. */
145
- status?: number;
146
- /** The specific signal that matched (page title, marker selector, or status). */
147
- signal?: string;
148
- }
149
- interface ScanResult {
150
- url: string;
151
- timestamp: string;
152
- /**
153
- * Set when the target served a bot-challenge/interstitial or an HTTP error
154
- * instead of the real page. When present, treat `issues`/`incomplete` as
155
- * "not scanned" (they are empty), NOT as "no problems found".
156
- */
157
- blocked?: BlockedInfo;
158
- /** High-confidence violations. Safe to surface as bugs. */
159
- issues: ScanIssue[];
160
- /** Detectors that hit their per-page cap this run — their counts are lower
161
- * bounds, not a census. Additive field; absent = nothing truncated. */
162
- truncated?: Array<{
163
- detector: string;
164
- cap: number;
165
- }>;
166
- /**
167
- * Non-fatal engine subsystem failures that silently disable a category of
168
- * checks. Consumers MUST surface these to the user — a result with
169
- * `engineWarnings` is NOT a full scan. Currently: `'color:kernel-injection-failed'`
170
- * disables all contrast checks for the page. Absent = all subsystems healthy.
171
- */
172
- engineWarnings?: string[];
173
- /** Engine (core package) version that produced this result — provenance */
174
- engine?: string;
175
- /**
176
- * Findings the classifier or axe-core flagged as ambiguous. Show to the user
177
- * for review — never auto-fix. Empty array (not omitted) when no incompletes,
178
- * so consumers can rely on the field.
179
- */
180
- incomplete: ScanIssue[];
181
- summary: {
182
- total: number;
183
- critical: number;
184
- serious: number;
185
- moderate: number;
186
- minor: number;
187
- /** Count of items in `incomplete[]`. */
188
- incomplete: number;
189
- };
190
- axeResults?: unknown;
191
- }
192
- type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
193
- declare const VIEWPORT_SIZES: Record<ViewportPreset, {
194
- width: number;
195
- height: number;
196
- }>;
197
- interface ScanOptions {
198
- wcagTags?: string[];
199
- includeAxe?: boolean;
200
- deep?: boolean;
201
- deepApiUrl?: string;
202
- deepApiKey?: string;
203
- dismissModals?: boolean;
204
- viewport?: ViewportPreset | {
205
- width: number;
206
- height: number;
207
- };
208
- browser?: {
209
- headless?: boolean;
210
- timeout?: number;
211
- };
212
- review?: boolean;
213
- /** Limit scan to elements within this CSS selector. */
214
- rootSelector?: string;
215
- /** Include annotated screenshots of violations in results. */
216
- enableScreenshots?: boolean;
217
- /** Scan content inside iframes. Default: false. */
218
- includeIframes?: boolean;
219
- /**
220
- * Wait for entrance animations to finish before scanning (prevents false
221
- * contrast failures on mid-fade text). `false` disables, a number sets the
222
- * wait cap in ms. Default: enabled with a 4000ms cap.
223
- */
224
- settle?: boolean | number;
225
- /**
226
- * HTTP status of the main-document response, for the Page-object call shape
227
- * where the CALLER navigated (e.g. mcp's flow_scan does its own `page.goto`).
228
- * The URL-string path captures this itself; on the Page path core cannot see
229
- * the response, so a plain 4xx/5xx error page with no challenge DOM markers
230
- * would otherwise be scanned as a clean page. Pass the caller's
231
- * `response.status()` here so bot-challenge/error-page detection works
232
- * uniformly across both entrypoints. Ignored on the URL-string path.
233
- */
234
- mainStatus?: number;
235
- }
1
+ import { P as PageLike, a as ScanOptions, S as ScanResult, b as SiteTheme, D as DetectedIssue, c as ScanIssue } from './types-10iqML3X.js';
2
+ export { B as BrowserDetector, F as FixDetector, d as FixIssueType, I as IssueImpact, e as IssueSource, V as VIEWPORT_SIZES, f as ViewportPreset, W as WCAG_BY_ISSUE, g as WcagLevel } from './types-10iqML3X.js';
236
3
 
237
4
  declare function scan(target: string | PageLike, options?: ScanOptions): Promise<ScanResult>;
238
5
 
@@ -246,7 +13,17 @@ type TruncationEntry = {
246
13
  * injection blocked by CSP) that silently disable a category of checks.
247
14
  * Both arrays are per-invocation (caller-owned), never module state.
248
15
  */
249
- declare function runDetectors(page: PageLike, outTruncated?: TruncationEntry[], outWarnings?: string[]): Promise<DetectedIssue[]>;
16
+ interface RunDetectorsOptions {
17
+ /**
18
+ * Skip the two serial FOCUS-MUTATING detectors (focusVisible moves real
19
+ * focus element-by-element; focusObscured scrolls anchor targets). Set by
20
+ * DOM-mode consumers scanning a page the user is LOOKING AT — stealing
21
+ * focus and scrolling their live tab is not acceptable there, and without
22
+ * page.keyboard those checks mostly land in needs-review anyway.
23
+ */
24
+ skipFocusMutating?: boolean;
25
+ }
26
+ declare function runDetectors(page: PageLike, outTruncated?: TruncationEntry[], outWarnings?: string[], options?: RunDetectorsOptions): Promise<DetectedIssue[]>;
250
27
  declare function extractSiteTheme(page: PageLike): Promise<SiteTheme>;
251
28
 
252
29
  interface AxeViolation {
@@ -348,4 +125,4 @@ interface DevSuggestion {
348
125
  }
349
126
  declare function generateDevSuggestion(issue: ScanIssue, framework: Framework, elementClasses?: string, fgColor?: string, bgColor?: string): DevSuggestion;
350
127
 
351
- export { type BrowserDetector, type ContrastSuggestion, type DetectedIssue, type DevSuggestion, type FixDetector, type FixIssueType, type Framework, type FrameworkInfo, type IssueImpact, type IssueSource, type PageLike, type ScanIssue, type ScanOptions, type ScanResult, type SiteTheme, type TailwindSuggestion, VIEWPORT_SIZES, type ViewportPreset, WCAG_BY_ISSUE, type WcagLevel, detectFramework, dismissModals, extractSiteTheme, generateDevSuggestion, getContrastRatio, runAxe, runDetectors, scan, solveContrast, suggestTailwindFix, toHtmlReport };
128
+ export { type ContrastSuggestion, DetectedIssue, type DevSuggestion, type Framework, type FrameworkInfo, PageLike, ScanIssue, ScanOptions, ScanResult, SiteTheme, type TailwindSuggestion, detectFramework, dismissModals, extractSiteTheme, generateDevSuggestion, getContrastRatio, runAxe, runDetectors, scan, solveContrast, suggestTailwindFix, toHtmlReport };