@webability/core 1.0.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.
@@ -0,0 +1,164 @@
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
+ /** All fix issue types the pipeline can detect and fix. */
16
+ 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';
17
+ /** WCAG criterion for each issue type. */
18
+ declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
19
+ /** A single accessibility issue detected on the page. */
20
+ interface DetectedIssue {
21
+ /** Unique CSS selector targeting the element. */
22
+ selector: string;
23
+ /** HTML/ARIA attribute to set (alt, aria-label, role, tabindex, etc.) */
24
+ attribute: string;
25
+ /** What the element currently has (empty string if missing). */
26
+ currentValue: string;
27
+ /** Classification of the issue. */
28
+ issueType: FixIssueType;
29
+ /** WCAG success criterion reference. */
30
+ wcag: string;
31
+ /** Contextual info for Claude: nearby heading, parent tag, href, page section. */
32
+ context: string;
33
+ /** true = needs Claude to generate the fix value; false = rule already knows it. */
34
+ needsAI: boolean;
35
+ /** When needsAI is false, this is the known fix value. */
36
+ fixValue?: string;
37
+ /** Optional: image src for alt text generation via vision. */
38
+ imageSrc?: string;
39
+ /** Optional: outer HTML snippet (first 300 chars) for Claude context. */
40
+ outerHtml?: string;
41
+ }
42
+ /** Every fix detector must implement this interface. */
43
+ interface FixDetector {
44
+ name: string;
45
+ detect(page: PageLike): Promise<DetectedIssue[]>;
46
+ }
47
+ /**
48
+ * Browser-runtime detector signature.
49
+ *
50
+ * Each detector exports a pure, zero-argument browser function that uses the
51
+ * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
52
+ * Node imports, no closures over module-scope variables — so it both:
53
+ * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
54
+ * stringifies and re-parses the function inside the browser context).
55
+ * 2. Bundles directly into the widget via webpack and runs against the
56
+ * live customer DOM.
57
+ *
58
+ * Single source of truth — same rules run in both runtimes.
59
+ */
60
+ type BrowserDetector = () => DetectedIssue[];
61
+ interface SiteTheme {
62
+ cssVars: Record<string, string>;
63
+ topBgColors: Array<{
64
+ color: string;
65
+ count: number;
66
+ }>;
67
+ topTextColors: Array<{
68
+ color: string;
69
+ count: number;
70
+ }>;
71
+ }
72
+ type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
73
+ type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
74
+ type WcagLevel = 'A' | 'AA' | 'AAA';
75
+ interface ScanIssue {
76
+ id: string;
77
+ source: IssueSource;
78
+ type: FixIssueType | string;
79
+ wcag: string;
80
+ level: WcagLevel;
81
+ impact: IssueImpact;
82
+ message: string;
83
+ selector: string;
84
+ html?: string;
85
+ fix?: {
86
+ attribute: string;
87
+ currentValue: string;
88
+ suggestedValue?: string;
89
+ needsManualReview: boolean;
90
+ };
91
+ }
92
+ interface ScanResult {
93
+ url: string;
94
+ timestamp: string;
95
+ issues: ScanIssue[];
96
+ summary: {
97
+ total: number;
98
+ critical: number;
99
+ serious: number;
100
+ moderate: number;
101
+ minor: number;
102
+ };
103
+ axeResults?: unknown;
104
+ }
105
+ type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
106
+ declare const VIEWPORT_SIZES: Record<ViewportPreset, {
107
+ width: number;
108
+ height: number;
109
+ }>;
110
+ interface ScanOptions {
111
+ wcagTags?: string[];
112
+ includeAxe?: boolean;
113
+ deep?: boolean;
114
+ deepApiUrl?: string;
115
+ deepApiKey?: string;
116
+ dismissModals?: boolean;
117
+ viewport?: ViewportPreset | {
118
+ width: number;
119
+ height: number;
120
+ };
121
+ browser?: {
122
+ headless?: boolean;
123
+ timeout?: number;
124
+ };
125
+ review?: boolean;
126
+ /** Limit scan to elements within this CSS selector. */
127
+ rootSelector?: string;
128
+ /** Include annotated screenshots of violations in results. */
129
+ enableScreenshots?: boolean;
130
+ /** Scan content inside iframes. Default: false. */
131
+ includeIframes?: boolean;
132
+ }
133
+
134
+ declare function scan(target: string | PageLike, options?: ScanOptions): Promise<ScanResult>;
135
+
136
+ declare function runDetectors(page: PageLike): Promise<DetectedIssue[]>;
137
+ declare function extractSiteTheme(page: PageLike): Promise<SiteTheme>;
138
+
139
+ interface AxeViolation {
140
+ id: string;
141
+ impact: string;
142
+ description: string;
143
+ help: string;
144
+ helpUrl: string;
145
+ tags: string[];
146
+ nodes: Array<{
147
+ html: string;
148
+ target: string[];
149
+ failureSummary?: string;
150
+ }>;
151
+ }
152
+ interface AxeRunResult {
153
+ violations: AxeViolation[];
154
+ incomplete: AxeViolation[];
155
+ passes: AxeViolation[];
156
+ inapplicable: AxeViolation[];
157
+ }
158
+ declare function runAxe(page: PageLike, tags: string[]): Promise<AxeRunResult>;
159
+
160
+ declare function dismissModals(page: PageLike): Promise<void>;
161
+
162
+ declare function toHtmlReport(result: ScanResult): string;
163
+
164
+ export { type BrowserDetector, type DetectedIssue, type FixDetector, type FixIssueType, type IssueImpact, type IssueSource, type PageLike, type ScanIssue, type ScanOptions, type ScanResult, type SiteTheme, VIEWPORT_SIZES, type ViewportPreset, WCAG_BY_ISSUE, type WcagLevel, dismissModals, extractSiteTheme, runAxe, runDetectors, scan, toHtmlReport };
@@ -0,0 +1,164 @@
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
+ /** All fix issue types the pipeline can detect and fix. */
16
+ 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';
17
+ /** WCAG criterion for each issue type. */
18
+ declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
19
+ /** A single accessibility issue detected on the page. */
20
+ interface DetectedIssue {
21
+ /** Unique CSS selector targeting the element. */
22
+ selector: string;
23
+ /** HTML/ARIA attribute to set (alt, aria-label, role, tabindex, etc.) */
24
+ attribute: string;
25
+ /** What the element currently has (empty string if missing). */
26
+ currentValue: string;
27
+ /** Classification of the issue. */
28
+ issueType: FixIssueType;
29
+ /** WCAG success criterion reference. */
30
+ wcag: string;
31
+ /** Contextual info for Claude: nearby heading, parent tag, href, page section. */
32
+ context: string;
33
+ /** true = needs Claude to generate the fix value; false = rule already knows it. */
34
+ needsAI: boolean;
35
+ /** When needsAI is false, this is the known fix value. */
36
+ fixValue?: string;
37
+ /** Optional: image src for alt text generation via vision. */
38
+ imageSrc?: string;
39
+ /** Optional: outer HTML snippet (first 300 chars) for Claude context. */
40
+ outerHtml?: string;
41
+ }
42
+ /** Every fix detector must implement this interface. */
43
+ interface FixDetector {
44
+ name: string;
45
+ detect(page: PageLike): Promise<DetectedIssue[]>;
46
+ }
47
+ /**
48
+ * Browser-runtime detector signature.
49
+ *
50
+ * Each detector exports a pure, zero-argument browser function that uses the
51
+ * `document` global directly and returns DetectedIssue[]. No Puppeteer, no
52
+ * Node imports, no closures over module-scope variables — so it both:
53
+ * 1. Survives serialization via `page.evaluate(fn)` on the server (puppeteer
54
+ * stringifies and re-parses the function inside the browser context).
55
+ * 2. Bundles directly into the widget via webpack and runs against the
56
+ * live customer DOM.
57
+ *
58
+ * Single source of truth — same rules run in both runtimes.
59
+ */
60
+ type BrowserDetector = () => DetectedIssue[];
61
+ interface SiteTheme {
62
+ cssVars: Record<string, string>;
63
+ topBgColors: Array<{
64
+ color: string;
65
+ count: number;
66
+ }>;
67
+ topTextColors: Array<{
68
+ color: string;
69
+ count: number;
70
+ }>;
71
+ }
72
+ type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
73
+ type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
74
+ type WcagLevel = 'A' | 'AA' | 'AAA';
75
+ interface ScanIssue {
76
+ id: string;
77
+ source: IssueSource;
78
+ type: FixIssueType | string;
79
+ wcag: string;
80
+ level: WcagLevel;
81
+ impact: IssueImpact;
82
+ message: string;
83
+ selector: string;
84
+ html?: string;
85
+ fix?: {
86
+ attribute: string;
87
+ currentValue: string;
88
+ suggestedValue?: string;
89
+ needsManualReview: boolean;
90
+ };
91
+ }
92
+ interface ScanResult {
93
+ url: string;
94
+ timestamp: string;
95
+ issues: ScanIssue[];
96
+ summary: {
97
+ total: number;
98
+ critical: number;
99
+ serious: number;
100
+ moderate: number;
101
+ minor: number;
102
+ };
103
+ axeResults?: unknown;
104
+ }
105
+ type ViewportPreset = 'mobile' | 'tablet' | 'desktop';
106
+ declare const VIEWPORT_SIZES: Record<ViewportPreset, {
107
+ width: number;
108
+ height: number;
109
+ }>;
110
+ interface ScanOptions {
111
+ wcagTags?: string[];
112
+ includeAxe?: boolean;
113
+ deep?: boolean;
114
+ deepApiUrl?: string;
115
+ deepApiKey?: string;
116
+ dismissModals?: boolean;
117
+ viewport?: ViewportPreset | {
118
+ width: number;
119
+ height: number;
120
+ };
121
+ browser?: {
122
+ headless?: boolean;
123
+ timeout?: number;
124
+ };
125
+ review?: boolean;
126
+ /** Limit scan to elements within this CSS selector. */
127
+ rootSelector?: string;
128
+ /** Include annotated screenshots of violations in results. */
129
+ enableScreenshots?: boolean;
130
+ /** Scan content inside iframes. Default: false. */
131
+ includeIframes?: boolean;
132
+ }
133
+
134
+ declare function scan(target: string | PageLike, options?: ScanOptions): Promise<ScanResult>;
135
+
136
+ declare function runDetectors(page: PageLike): Promise<DetectedIssue[]>;
137
+ declare function extractSiteTheme(page: PageLike): Promise<SiteTheme>;
138
+
139
+ interface AxeViolation {
140
+ id: string;
141
+ impact: string;
142
+ description: string;
143
+ help: string;
144
+ helpUrl: string;
145
+ tags: string[];
146
+ nodes: Array<{
147
+ html: string;
148
+ target: string[];
149
+ failureSummary?: string;
150
+ }>;
151
+ }
152
+ interface AxeRunResult {
153
+ violations: AxeViolation[];
154
+ incomplete: AxeViolation[];
155
+ passes: AxeViolation[];
156
+ inapplicable: AxeViolation[];
157
+ }
158
+ declare function runAxe(page: PageLike, tags: string[]): Promise<AxeRunResult>;
159
+
160
+ declare function dismissModals(page: PageLike): Promise<void>;
161
+
162
+ declare function toHtmlReport(result: ScanResult): string;
163
+
164
+ export { type BrowserDetector, type DetectedIssue, type FixDetector, type FixIssueType, type IssueImpact, type IssueSource, type PageLike, type ScanIssue, type ScanOptions, type ScanResult, type SiteTheme, VIEWPORT_SIZES, type ViewportPreset, WCAG_BY_ISSUE, type WcagLevel, dismissModals, extractSiteTheme, runAxe, runDetectors, scan, toHtmlReport };