@webability/core 1.10.0 → 1.12.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.
@@ -219,7 +219,7 @@ interface PageLike {
219
219
  axePage?(): unknown;
220
220
  }
221
221
  /** All fix issue types the pipeline can detect and fix. */
222
- 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_document_language' | 'missing_landmark' | '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' | 'form_missing_submit' | 'decorative_image_with_title' | 'placeholder_link' | 'missing_pattern_title' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'zoom_restriction' | 'missing_button_type' | 'link_consistency' | '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' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'empty_heading' | 'javascript_link' | 'broken_aria_reference' | 'main_content_hidden' | 'scrollable_not_focusable' | 'meaningful_sequence' | 'sensory_characteristic' | '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' | 'inconsistent_navigation';
222
+ type FixIssueType = 'weak_link_name' | 'weak_heading' | 'weak_iframe_title' | 'weak_page_title' | 'weak_svg_name' | 'missing_button_role' | 'missing_semantic_role' | 'missing_document_language' | 'svg_missing_name' | 'aria_hidden_focusable' | 'contrast_insufficient' | 'non_text_contrast_insufficient' | 'focus_not_visible' | 'empty_link' | 'unlabeled_button' | 'missing_label' | 'skip_link' | 'aria_live' | 'missing_alt' | 'identify_input_purpose' | 'redundant_role' | 'new_window_link' | 'missing_fieldset_legend' | 'missing_table_header' | 'missing_table_caption' | 'missing_table_scope' | 'external_link_warning' | 'download_link_warning' | 'form_missing_submit' | 'placeholder_link' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'zoom_restriction' | 'resize_text_clipped' | 'link_consistency' | 'keyboard_trap' | 'keyboard_trap_self_focus' | 'focus_order_disruption' | 'missing_captions' | 'color_only_link' | 'label_in_name' | 'onfocus_context_change' | 'focus_obscured_by_sticky' | 'text_spacing_blocked' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'carousel_autoplay_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'broken_aria_reference' | 'main_content_hidden' | 'meaningful_sequence' | 'sensory_characteristic' | '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' | 'inconsistent_navigation' | 'adjacent_redundant_link' | 'missing_button_type' | 'decorative_image_with_title' | 'nested_landmark';
223
223
  /** WCAG criterion for each issue type. */
224
224
  declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
225
225
  /** A single accessibility issue detected on the page. */
@@ -258,6 +258,12 @@ interface DetectedIssue {
258
258
  manualFix?: boolean;
259
259
  /** Why this finding needs review — surfaced to MCP/SDK consumers. */
260
260
  reviewReason?: string;
261
+ /**
262
+ * State pseudo-class the fix applies in (`:focus-visible` for
263
+ * focus_not_visible). `selector` always names the element at rest; a
264
+ * consumer that writes CSS appends this to the selector.
265
+ */
266
+ pseudo?: string;
261
267
  /**
262
268
  * Optional exact user-facing message. Use when the generic
263
269
  * attribute/currentValue template ("missing X", "X is \"Y\"") would state
@@ -366,10 +372,13 @@ interface JudgedEntry {
366
372
  * injected nodes changed (the scanner restored it)
367
373
  * - finding-dropped: a finding sat on an injected (marked) node or on an
368
374
  * element a tool left focused; it was not reported
375
+ * - engine-failed: an engine could not read the page (a detector, axe, or the
376
+ * page itself was lost mid-scan) and threw. The findings it would have
377
+ * produced are absent, so the result must not be read as clean for them.
369
378
  */
370
379
  interface InternalBug {
371
380
  tool: string;
372
- kind: 'state-not-restored' | 'finding-dropped';
381
+ kind: 'state-not-restored' | 'finding-dropped' | 'engine-failed';
373
382
  detail: string;
374
383
  selector?: string;
375
384
  /**
@@ -422,6 +431,12 @@ interface ScanResult {
422
431
  internal_bugs?: InternalBug[];
423
432
  /** Engine (core package) version that produced this result — provenance */
424
433
  engine?: string;
434
+ /**
435
+ * @internal The WebAbility detectors' raw rows, before conversion, judging,
436
+ * dedup and level gating. Present only with `keepDetected`. Not findings: a
437
+ * row here may have been dropped from `issues`.
438
+ */
439
+ detected?: DetectedIssue[];
425
440
  /**
426
441
  * @deprecated Always `[]`. The scanner judges uncertain findings itself:
427
442
  * each becomes a verdict in `issues` or is dropped. The field stays only so
@@ -455,12 +470,25 @@ interface ScanOptions {
455
470
  wcagTags?: string[];
456
471
  /** @internal test seam: extra detectors run serially, each in its own state guard. */
457
472
  extraSerialDetectors?: FixDetector[];
473
+ /**
474
+ * @internal Return the WebAbility detectors' raw rows as `ScanResult.detected`
475
+ * (runEngine joins them to the reported issues for the platform). Default off:
476
+ * the result carries no `detected` key.
477
+ */
478
+ keepDetected?: boolean;
458
479
  /**
459
480
  * Highest WCAG conformance level to report. Default 'AA': findings whose
460
481
  * criteria are all Level AAA are not reported anywhere (issues, incomplete,
461
482
  * summary). 'AAA' opts in (and adds axe's wcag2aaa rules); 'A' keeps Level A only.
462
483
  */
463
484
  level?: WcagLevel;
485
+ /**
486
+ * Also run the experimental best-practice checks (default false). They are
487
+ * not WCAG 2.2 A/AA requirements, so the default scan never runs them:
488
+ * adjacent_redundant_link, missing_button_type, decorative_image_with_title,
489
+ * nested_landmark.
490
+ */
491
+ experimental?: boolean;
464
492
  /**
465
493
  * Run axe-core (default true). axe is the only engine for the checks it
466
494
  * covers on the ACT test suite: form-field / select / combobox names and
@@ -219,7 +219,7 @@ interface PageLike {
219
219
  axePage?(): unknown;
220
220
  }
221
221
  /** All fix issue types the pipeline can detect and fix. */
222
- 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_document_language' | 'missing_landmark' | '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' | 'form_missing_submit' | 'decorative_image_with_title' | 'placeholder_link' | 'missing_pattern_title' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'zoom_restriction' | 'missing_button_type' | 'link_consistency' | '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' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'empty_heading' | 'javascript_link' | 'broken_aria_reference' | 'main_content_hidden' | 'scrollable_not_focusable' | 'meaningful_sequence' | 'sensory_characteristic' | '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' | 'inconsistent_navigation';
222
+ type FixIssueType = 'weak_link_name' | 'weak_heading' | 'weak_iframe_title' | 'weak_page_title' | 'weak_svg_name' | 'missing_button_role' | 'missing_semantic_role' | 'missing_document_language' | 'svg_missing_name' | 'aria_hidden_focusable' | 'contrast_insufficient' | 'non_text_contrast_insufficient' | 'focus_not_visible' | 'empty_link' | 'unlabeled_button' | 'missing_label' | 'skip_link' | 'aria_live' | 'missing_alt' | 'identify_input_purpose' | 'redundant_role' | 'new_window_link' | 'missing_fieldset_legend' | 'missing_table_header' | 'missing_table_caption' | 'missing_table_scope' | 'external_link_warning' | 'download_link_warning' | 'form_missing_submit' | 'placeholder_link' | 'missing_autocomplete' | 'missing_media_controls' | 'unsafe_autoplay' | 'zoom_restriction' | 'resize_text_clipped' | 'link_consistency' | 'keyboard_trap' | 'keyboard_trap_self_focus' | 'focus_order_disruption' | 'missing_captions' | 'color_only_link' | 'label_in_name' | 'onfocus_context_change' | 'focus_obscured_by_sticky' | 'text_spacing_blocked' | 'motion_uncontrollable' | 'auto_refresh' | 'motion_without_reduced_motion' | 'flashing_content' | 'animated_gif_no_pause' | 'carousel_autoplay_no_pause' | 'missing_required_indicator' | 'background_image_unlabeled' | 'broken_aria_reference' | 'main_content_hidden' | 'meaningful_sequence' | 'sensory_characteristic' | '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' | 'inconsistent_navigation' | 'adjacent_redundant_link' | 'missing_button_type' | 'decorative_image_with_title' | 'nested_landmark';
223
223
  /** WCAG criterion for each issue type. */
224
224
  declare const WCAG_BY_ISSUE: Record<FixIssueType, string>;
225
225
  /** A single accessibility issue detected on the page. */
@@ -258,6 +258,12 @@ interface DetectedIssue {
258
258
  manualFix?: boolean;
259
259
  /** Why this finding needs review — surfaced to MCP/SDK consumers. */
260
260
  reviewReason?: string;
261
+ /**
262
+ * State pseudo-class the fix applies in (`:focus-visible` for
263
+ * focus_not_visible). `selector` always names the element at rest; a
264
+ * consumer that writes CSS appends this to the selector.
265
+ */
266
+ pseudo?: string;
261
267
  /**
262
268
  * Optional exact user-facing message. Use when the generic
263
269
  * attribute/currentValue template ("missing X", "X is \"Y\"") would state
@@ -366,10 +372,13 @@ interface JudgedEntry {
366
372
  * injected nodes changed (the scanner restored it)
367
373
  * - finding-dropped: a finding sat on an injected (marked) node or on an
368
374
  * element a tool left focused; it was not reported
375
+ * - engine-failed: an engine could not read the page (a detector, axe, or the
376
+ * page itself was lost mid-scan) and threw. The findings it would have
377
+ * produced are absent, so the result must not be read as clean for them.
369
378
  */
370
379
  interface InternalBug {
371
380
  tool: string;
372
- kind: 'state-not-restored' | 'finding-dropped';
381
+ kind: 'state-not-restored' | 'finding-dropped' | 'engine-failed';
373
382
  detail: string;
374
383
  selector?: string;
375
384
  /**
@@ -422,6 +431,12 @@ interface ScanResult {
422
431
  internal_bugs?: InternalBug[];
423
432
  /** Engine (core package) version that produced this result — provenance */
424
433
  engine?: string;
434
+ /**
435
+ * @internal The WebAbility detectors' raw rows, before conversion, judging,
436
+ * dedup and level gating. Present only with `keepDetected`. Not findings: a
437
+ * row here may have been dropped from `issues`.
438
+ */
439
+ detected?: DetectedIssue[];
425
440
  /**
426
441
  * @deprecated Always `[]`. The scanner judges uncertain findings itself:
427
442
  * each becomes a verdict in `issues` or is dropped. The field stays only so
@@ -455,12 +470,25 @@ interface ScanOptions {
455
470
  wcagTags?: string[];
456
471
  /** @internal test seam: extra detectors run serially, each in its own state guard. */
457
472
  extraSerialDetectors?: FixDetector[];
473
+ /**
474
+ * @internal Return the WebAbility detectors' raw rows as `ScanResult.detected`
475
+ * (runEngine joins them to the reported issues for the platform). Default off:
476
+ * the result carries no `detected` key.
477
+ */
478
+ keepDetected?: boolean;
458
479
  /**
459
480
  * Highest WCAG conformance level to report. Default 'AA': findings whose
460
481
  * criteria are all Level AAA are not reported anywhere (issues, incomplete,
461
482
  * summary). 'AAA' opts in (and adds axe's wcag2aaa rules); 'A' keeps Level A only.
462
483
  */
463
484
  level?: WcagLevel;
485
+ /**
486
+ * Also run the experimental best-practice checks (default false). They are
487
+ * not WCAG 2.2 A/AA requirements, so the default scan never runs them:
488
+ * adjacent_redundant_link, missing_button_type, decorative_image_with_title,
489
+ * nested_landmark.
490
+ */
491
+ experimental?: boolean;
464
492
  /**
465
493
  * Run axe-core (default true). axe is the only engine for the checks it
466
494
  * covers on the ACT test suite: form-field / select / combobox names and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webability/core",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Accessibility scanner engine — WCAG 2.2 AA compliance testing with 200+ rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -82,7 +82,9 @@
82
82
  "devDependencies": {
83
83
  "axe-core": "^4.11.3",
84
84
  "esbuild": "0.27.7",
85
+ "jsdom": "29.0.2",
85
86
  "playwright": "1.59.1",
87
+ "puppeteer-core": "24",
86
88
  "tsup": "^8.3.0",
87
89
  "typescript": "^5.6.0",
88
90
  "vitest": "^2.0.0"