@webability/core 1.14.0 → 1.16.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/engine.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { c as ScanIssue, m as JudgeOptions, I as InternalBug, q as JudgedEntry, P as PageLike } from './types-gjAJ8j0I.cjs';
1
+ import { c as ScanIssue, m as JudgeOptions, I as InternalBug, q as JudgedEntry, P as PageLike } from './types-Bk-B2Y9X.cjs';
2
+ import { d as FixJudgeTransport, f as FixJudgeVerdictKind } from './hostedFixJudge-DXYes7X1.cjs';
2
3
 
3
4
  /**
4
5
  * Visitor HTML (`input: { html, baseURL }`): what the page may fetch while the
@@ -217,6 +218,25 @@ interface EngineOptions {
217
218
  * structural fixes, `verification` absent.
218
219
  */
219
220
  verify?: boolean;
221
+ /**
222
+ * Hosted fix judge: the fixes the engine emits are sent to this injected
223
+ * transport (see `hostedFixJudgeTransport`) and come back as
224
+ * `EngineResult.judgedFixes`, one verdict per fix. The judge runs behind the
225
+ * transport, never in core. `fixes` itself is unchanged: the caller decides
226
+ * what an `apply: false` verdict means. Only for a page the engine loaded by
227
+ * URL (`{ url }` or `{ loaded: true }`): the judge loads that public URL
228
+ * itself, so html/snapshot input is skipped with a warning. `timeoutMs` bounds
229
+ * each call (up to ceil(fixes / 20) sequential calls), not the whole stage;
230
+ * default 160 s.
231
+ */
232
+ fixJudge?: {
233
+ transport: FixJudgeTransport;
234
+ viewport?: {
235
+ width: number;
236
+ height: number;
237
+ };
238
+ timeoutMs?: number;
239
+ };
220
240
  /** Axe runs verification may spend isolating regressions. Default 40. */
221
241
  verifyMaxAxeRuns?: number;
222
242
  /** HTTP status of the main document when the caller navigated. */
@@ -273,8 +293,11 @@ type EngineIssue = ScanIssue;
273
293
  * A WebAbility detector's own record of a reported issue: the fields scan()'s
274
294
  * issue row drops, for a consumer that writes values itself (the platform's
275
295
  * DetectedIssue). One per `source: 'webability'` issue in `issues[]`, same
276
- * `id`; axe-core rows have none. Only issues that survived scan()'s judging,
277
- * dedup and level gate get one.
296
+ * `id`; axe-core rows have none, except `target-size` (WCAG 2.5.8): an axe row
297
+ * with a checked min-size fix gets a synthetic record (issueType
298
+ * `target_too_small`, attribute `style`, fixValue the write, same `id` as the axe
299
+ * row) so a consumer that joins fixes through findings[] sees it. Only issues
300
+ * that survived scan()'s judging, dedup and level gate get one.
278
301
  */
279
302
  interface EngineFinding {
280
303
  /** The issue row's id (`issues[k].id`). */
@@ -332,6 +355,8 @@ interface EngineStats {
332
355
  * rejected candidate never blocks a valid one.
333
356
  * `rejectedByEmptyValue`: a detector fix with an empty value on an attribute
334
357
  * other than alt (a boolean attribute such as controls=""), why `empty_value`.
358
+ * `targetSizeSkipped`: axe target-size findings with no safe min-size fix, by skip reason
359
+ * (informational: the findings stay; not a degraded stage and not part of the identity above).
335
360
  */
336
361
  fixes: {
337
362
  candidates: number;
@@ -345,6 +370,7 @@ interface EngineStats {
345
370
  rejectedByEmptyValue: number;
346
371
  rejectedByNeverHarm: number;
347
372
  rejectedByVerify: number;
373
+ targetSizeSkipped: Record<string, number>;
348
374
  rejected: Array<{
349
375
  sel: string;
350
376
  attr: string;
@@ -384,6 +410,18 @@ interface EngineStats {
384
410
  /** Capabilities the injected Page lacked and what was skipped because of it. */
385
411
  warnings: string[];
386
412
  }
413
+ /** One emitted fix with the hosted judge's verdict. `apply` is true only for `correct_needed`; every failure is `unclear` + `apply: false`. */
414
+ interface JudgedFix {
415
+ sel: string;
416
+ attr: string;
417
+ val: string;
418
+ issueType: string;
419
+ verdict: FixJudgeVerdictKind;
420
+ apply: boolean;
421
+ /** wrong_value only: the value the judge says would be right. */
422
+ suggestedValue?: string;
423
+ reason: string;
424
+ }
387
425
  interface EngineResult {
388
426
  url: string;
389
427
  issues: EngineIssue[];
@@ -393,6 +431,8 @@ interface EngineResult {
393
431
  findings: EngineFinding[];
394
432
  internal_bugs: InternalBug[];
395
433
  stats: EngineStats;
434
+ /** The hosted fix judge's verdict on each emitted fix, in `fixes` order (`opts.fixJudge`). Absent when no judge was set or it was skipped (see `stats.warnings`). */
435
+ judgedFixes?: JudgedFix[];
396
436
  /** Verdicts of core's in-scan judge (`opts.scan.judge`) on findings the engines could not decide. */
397
437
  judged?: JudgedEntry[];
398
438
  /** Set when the target was a bot challenge, auth wall or HTTP error: nothing real was scanned. */
@@ -843,4 +883,80 @@ interface SelectorQualityResult {
843
883
  declare const BARE_TAGS: Set<string>;
844
884
  declare function scoreSelectorQuality(selector: string): SelectorQualityResult;
845
885
 
846
- export { BARE_TAGS, type EngineFinding, type EngineFix, type EngineInput, type EngineIssue, type EngineOptions, type EngineResult, type EngineStats, FORM_CONTROL_ROLES, type HazardElementView, type HtmlDecision, type HtmlRequest, type HtmlRequestPolicy, InternalBug, LABEL_IN_NAME_JS, type Model, type ModelRequest, type ModelResponse, type Page, type PlaywrightPageLike, type ProtectedScope, type PuppeteerPageLike, SERVED_FIX_KEYS, TOKEN_QUERY_KEY, classifyFixRisk, detectPageLanguage, dropHazardousHidingFixes, foldContains, foldLabel, fromPlaywright, fromPuppeteer, htmlRequestPolicy, isHidingFix, isHtmlLangFix, isInteractiveOrContainsFocusable, isLabelText, labelContainsText, labelInNameCheck, languageFromText, languageFromUrl, loadSnapshot, normalizeVisibleText, parseContext, primaryLanguage, runEngine, scoreSelectorQuality, selectorParses, toPageLike, validateAttribute, validateFix, validateRoleValue, validateSelector, validateSelectorForAttribute, validateTextValue };
886
+ /**
887
+ * Hidden-at-rest text, listed as facts.
888
+ *
889
+ * Read-only and mechanical: this never clicks, hovers, scrolls, focuses or screenshots, and it decides nothing
890
+ * about whether a user can reach the content or whether it passes. It reports what the page says about itself:
891
+ * why a text group is not painted at rest, what the CSS backdrop is, and which interactive elements sit near it.
892
+ * A caller that has a browser and a judge (the platform lane) decides how to reveal a candidate and what it measures.
893
+ */
894
+
895
+ type HiddenKind = 'display' | 'visibility' | 'opacity' | 'offscreen' | 'overflow-clip' | 'zero-size';
896
+ type BackdropKind = 'plain' | 'image' | 'gradient' | 'sibling-layer' | 'unknown';
897
+ interface HiddenCause {
898
+ kind: HiddenKind;
899
+ /** The element that causes it (may be the text element itself). */
900
+ selector: string;
901
+ /** True when the element is moved out of view by a transform. */
902
+ transformed?: boolean;
903
+ }
904
+ interface HiddenText {
905
+ selector: string;
906
+ text: string;
907
+ fg: string | null;
908
+ /** Plain CSS backdrop colour when one is known from the cascade. */
909
+ bg: string | null;
910
+ backdropKind: BackdropKind;
911
+ /** Contrast of fg on bg ignoring the hiding (opacity treated as 1). Null unless the backdrop is plain. */
912
+ staticRatio: number | null;
913
+ threshold: number;
914
+ rect: {
915
+ x: number;
916
+ y: number;
917
+ w: number;
918
+ h: number;
919
+ } | null;
920
+ }
921
+ interface NearbyInteractive {
922
+ selector: string;
923
+ tag: string;
924
+ role: string | null;
925
+ name: string;
926
+ hrefKind: 'none' | 'hash' | 'javascript' | 'same-page' | 'external';
927
+ inForm: boolean;
928
+ expanded: string | null;
929
+ selected: string | null;
930
+ current: string | null;
931
+ pressed: string | null;
932
+ /** Levels above the group root at which it was found (1 = a child of the root's parent). */
933
+ distance: number;
934
+ relation: 'ancestor' | 'in-ancestor';
935
+ }
936
+ interface HiddenCandidate {
937
+ id: number;
938
+ /** The topmost hidden ancestor-or-self of the texts. */
939
+ root: string;
940
+ hiddenBy: HiddenCause[];
941
+ ariaHidden: boolean;
942
+ inert: boolean;
943
+ texts: HiddenText[];
944
+ interactiveNearby: NearbyInteractive[];
945
+ }
946
+ interface HiddenCandidateList {
947
+ candidates: HiddenCandidate[];
948
+ /** Hidden groups found on the page, before the cap. */
949
+ found: number;
950
+ truncated?: {
951
+ found: number;
952
+ kept: number;
953
+ cap: number;
954
+ };
955
+ }
956
+ declare const DEFAULT_MAX_CANDIDATES = 200;
957
+ /** List hidden-at-rest text groups with facts. Read-only. */
958
+ declare function listHiddenCandidates(page: PageLike, opts?: {
959
+ max?: number;
960
+ }): Promise<HiddenCandidateList>;
961
+
962
+ export { BARE_TAGS, type BackdropKind, DEFAULT_MAX_CANDIDATES, type EngineFinding, type EngineFix, type EngineInput, type EngineIssue, type EngineOptions, type EngineResult, type EngineStats, FORM_CONTROL_ROLES, type HazardElementView, type HiddenCandidate, type HiddenCandidateList, type HiddenCause, type HiddenKind, type HiddenText, type HtmlDecision, type HtmlRequest, type HtmlRequestPolicy, InternalBug, type JudgedFix, LABEL_IN_NAME_JS, type Model, type ModelRequest, type ModelResponse, type NearbyInteractive, type Page, type PlaywrightPageLike, type ProtectedScope, type PuppeteerPageLike, SERVED_FIX_KEYS, TOKEN_QUERY_KEY, classifyFixRisk, detectPageLanguage, dropHazardousHidingFixes, foldContains, foldLabel, fromPlaywright, fromPuppeteer, htmlRequestPolicy, isHidingFix, isHtmlLangFix, isInteractiveOrContainsFocusable, isLabelText, labelContainsText, labelInNameCheck, languageFromText, languageFromUrl, listHiddenCandidates, loadSnapshot, normalizeVisibleText, parseContext, primaryLanguage, runEngine, scoreSelectorQuality, selectorParses, toPageLike, validateAttribute, validateFix, validateRoleValue, validateSelector, validateSelectorForAttribute, validateTextValue };
package/dist/engine.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { c as ScanIssue, m as JudgeOptions, I as InternalBug, q as JudgedEntry, P as PageLike } from './types-gjAJ8j0I.js';
1
+ import { c as ScanIssue, m as JudgeOptions, I as InternalBug, q as JudgedEntry, P as PageLike } from './types-Bk-B2Y9X.js';
2
+ import { d as FixJudgeTransport, f as FixJudgeVerdictKind } from './hostedFixJudge-DXYes7X1.js';
2
3
 
3
4
  /**
4
5
  * Visitor HTML (`input: { html, baseURL }`): what the page may fetch while the
@@ -217,6 +218,25 @@ interface EngineOptions {
217
218
  * structural fixes, `verification` absent.
218
219
  */
219
220
  verify?: boolean;
221
+ /**
222
+ * Hosted fix judge: the fixes the engine emits are sent to this injected
223
+ * transport (see `hostedFixJudgeTransport`) and come back as
224
+ * `EngineResult.judgedFixes`, one verdict per fix. The judge runs behind the
225
+ * transport, never in core. `fixes` itself is unchanged: the caller decides
226
+ * what an `apply: false` verdict means. Only for a page the engine loaded by
227
+ * URL (`{ url }` or `{ loaded: true }`): the judge loads that public URL
228
+ * itself, so html/snapshot input is skipped with a warning. `timeoutMs` bounds
229
+ * each call (up to ceil(fixes / 20) sequential calls), not the whole stage;
230
+ * default 160 s.
231
+ */
232
+ fixJudge?: {
233
+ transport: FixJudgeTransport;
234
+ viewport?: {
235
+ width: number;
236
+ height: number;
237
+ };
238
+ timeoutMs?: number;
239
+ };
220
240
  /** Axe runs verification may spend isolating regressions. Default 40. */
221
241
  verifyMaxAxeRuns?: number;
222
242
  /** HTTP status of the main document when the caller navigated. */
@@ -273,8 +293,11 @@ type EngineIssue = ScanIssue;
273
293
  * A WebAbility detector's own record of a reported issue: the fields scan()'s
274
294
  * issue row drops, for a consumer that writes values itself (the platform's
275
295
  * DetectedIssue). One per `source: 'webability'` issue in `issues[]`, same
276
- * `id`; axe-core rows have none. Only issues that survived scan()'s judging,
277
- * dedup and level gate get one.
296
+ * `id`; axe-core rows have none, except `target-size` (WCAG 2.5.8): an axe row
297
+ * with a checked min-size fix gets a synthetic record (issueType
298
+ * `target_too_small`, attribute `style`, fixValue the write, same `id` as the axe
299
+ * row) so a consumer that joins fixes through findings[] sees it. Only issues
300
+ * that survived scan()'s judging, dedup and level gate get one.
278
301
  */
279
302
  interface EngineFinding {
280
303
  /** The issue row's id (`issues[k].id`). */
@@ -332,6 +355,8 @@ interface EngineStats {
332
355
  * rejected candidate never blocks a valid one.
333
356
  * `rejectedByEmptyValue`: a detector fix with an empty value on an attribute
334
357
  * other than alt (a boolean attribute such as controls=""), why `empty_value`.
358
+ * `targetSizeSkipped`: axe target-size findings with no safe min-size fix, by skip reason
359
+ * (informational: the findings stay; not a degraded stage and not part of the identity above).
335
360
  */
336
361
  fixes: {
337
362
  candidates: number;
@@ -345,6 +370,7 @@ interface EngineStats {
345
370
  rejectedByEmptyValue: number;
346
371
  rejectedByNeverHarm: number;
347
372
  rejectedByVerify: number;
373
+ targetSizeSkipped: Record<string, number>;
348
374
  rejected: Array<{
349
375
  sel: string;
350
376
  attr: string;
@@ -384,6 +410,18 @@ interface EngineStats {
384
410
  /** Capabilities the injected Page lacked and what was skipped because of it. */
385
411
  warnings: string[];
386
412
  }
413
+ /** One emitted fix with the hosted judge's verdict. `apply` is true only for `correct_needed`; every failure is `unclear` + `apply: false`. */
414
+ interface JudgedFix {
415
+ sel: string;
416
+ attr: string;
417
+ val: string;
418
+ issueType: string;
419
+ verdict: FixJudgeVerdictKind;
420
+ apply: boolean;
421
+ /** wrong_value only: the value the judge says would be right. */
422
+ suggestedValue?: string;
423
+ reason: string;
424
+ }
387
425
  interface EngineResult {
388
426
  url: string;
389
427
  issues: EngineIssue[];
@@ -393,6 +431,8 @@ interface EngineResult {
393
431
  findings: EngineFinding[];
394
432
  internal_bugs: InternalBug[];
395
433
  stats: EngineStats;
434
+ /** The hosted fix judge's verdict on each emitted fix, in `fixes` order (`opts.fixJudge`). Absent when no judge was set or it was skipped (see `stats.warnings`). */
435
+ judgedFixes?: JudgedFix[];
396
436
  /** Verdicts of core's in-scan judge (`opts.scan.judge`) on findings the engines could not decide. */
397
437
  judged?: JudgedEntry[];
398
438
  /** Set when the target was a bot challenge, auth wall or HTTP error: nothing real was scanned. */
@@ -843,4 +883,80 @@ interface SelectorQualityResult {
843
883
  declare const BARE_TAGS: Set<string>;
844
884
  declare function scoreSelectorQuality(selector: string): SelectorQualityResult;
845
885
 
846
- export { BARE_TAGS, type EngineFinding, type EngineFix, type EngineInput, type EngineIssue, type EngineOptions, type EngineResult, type EngineStats, FORM_CONTROL_ROLES, type HazardElementView, type HtmlDecision, type HtmlRequest, type HtmlRequestPolicy, InternalBug, LABEL_IN_NAME_JS, type Model, type ModelRequest, type ModelResponse, type Page, type PlaywrightPageLike, type ProtectedScope, type PuppeteerPageLike, SERVED_FIX_KEYS, TOKEN_QUERY_KEY, classifyFixRisk, detectPageLanguage, dropHazardousHidingFixes, foldContains, foldLabel, fromPlaywright, fromPuppeteer, htmlRequestPolicy, isHidingFix, isHtmlLangFix, isInteractiveOrContainsFocusable, isLabelText, labelContainsText, labelInNameCheck, languageFromText, languageFromUrl, loadSnapshot, normalizeVisibleText, parseContext, primaryLanguage, runEngine, scoreSelectorQuality, selectorParses, toPageLike, validateAttribute, validateFix, validateRoleValue, validateSelector, validateSelectorForAttribute, validateTextValue };
886
+ /**
887
+ * Hidden-at-rest text, listed as facts.
888
+ *
889
+ * Read-only and mechanical: this never clicks, hovers, scrolls, focuses or screenshots, and it decides nothing
890
+ * about whether a user can reach the content or whether it passes. It reports what the page says about itself:
891
+ * why a text group is not painted at rest, what the CSS backdrop is, and which interactive elements sit near it.
892
+ * A caller that has a browser and a judge (the platform lane) decides how to reveal a candidate and what it measures.
893
+ */
894
+
895
+ type HiddenKind = 'display' | 'visibility' | 'opacity' | 'offscreen' | 'overflow-clip' | 'zero-size';
896
+ type BackdropKind = 'plain' | 'image' | 'gradient' | 'sibling-layer' | 'unknown';
897
+ interface HiddenCause {
898
+ kind: HiddenKind;
899
+ /** The element that causes it (may be the text element itself). */
900
+ selector: string;
901
+ /** True when the element is moved out of view by a transform. */
902
+ transformed?: boolean;
903
+ }
904
+ interface HiddenText {
905
+ selector: string;
906
+ text: string;
907
+ fg: string | null;
908
+ /** Plain CSS backdrop colour when one is known from the cascade. */
909
+ bg: string | null;
910
+ backdropKind: BackdropKind;
911
+ /** Contrast of fg on bg ignoring the hiding (opacity treated as 1). Null unless the backdrop is plain. */
912
+ staticRatio: number | null;
913
+ threshold: number;
914
+ rect: {
915
+ x: number;
916
+ y: number;
917
+ w: number;
918
+ h: number;
919
+ } | null;
920
+ }
921
+ interface NearbyInteractive {
922
+ selector: string;
923
+ tag: string;
924
+ role: string | null;
925
+ name: string;
926
+ hrefKind: 'none' | 'hash' | 'javascript' | 'same-page' | 'external';
927
+ inForm: boolean;
928
+ expanded: string | null;
929
+ selected: string | null;
930
+ current: string | null;
931
+ pressed: string | null;
932
+ /** Levels above the group root at which it was found (1 = a child of the root's parent). */
933
+ distance: number;
934
+ relation: 'ancestor' | 'in-ancestor';
935
+ }
936
+ interface HiddenCandidate {
937
+ id: number;
938
+ /** The topmost hidden ancestor-or-self of the texts. */
939
+ root: string;
940
+ hiddenBy: HiddenCause[];
941
+ ariaHidden: boolean;
942
+ inert: boolean;
943
+ texts: HiddenText[];
944
+ interactiveNearby: NearbyInteractive[];
945
+ }
946
+ interface HiddenCandidateList {
947
+ candidates: HiddenCandidate[];
948
+ /** Hidden groups found on the page, before the cap. */
949
+ found: number;
950
+ truncated?: {
951
+ found: number;
952
+ kept: number;
953
+ cap: number;
954
+ };
955
+ }
956
+ declare const DEFAULT_MAX_CANDIDATES = 200;
957
+ /** List hidden-at-rest text groups with facts. Read-only. */
958
+ declare function listHiddenCandidates(page: PageLike, opts?: {
959
+ max?: number;
960
+ }): Promise<HiddenCandidateList>;
961
+
962
+ export { BARE_TAGS, type BackdropKind, DEFAULT_MAX_CANDIDATES, type EngineFinding, type EngineFix, type EngineInput, type EngineIssue, type EngineOptions, type EngineResult, type EngineStats, FORM_CONTROL_ROLES, type HazardElementView, type HiddenCandidate, type HiddenCandidateList, type HiddenCause, type HiddenKind, type HiddenText, type HtmlDecision, type HtmlRequest, type HtmlRequestPolicy, InternalBug, type JudgedFix, LABEL_IN_NAME_JS, type Model, type ModelRequest, type ModelResponse, type NearbyInteractive, type Page, type PlaywrightPageLike, type ProtectedScope, type PuppeteerPageLike, SERVED_FIX_KEYS, TOKEN_QUERY_KEY, classifyFixRisk, detectPageLanguage, dropHazardousHidingFixes, foldContains, foldLabel, fromPlaywright, fromPuppeteer, htmlRequestPolicy, isHidingFix, isHtmlLangFix, isInteractiveOrContainsFocusable, isLabelText, labelContainsText, labelInNameCheck, languageFromText, languageFromUrl, listHiddenCandidates, loadSnapshot, normalizeVisibleText, parseContext, primaryLanguage, runEngine, scoreSelectorQuality, selectorParses, toPageLike, validateAttribute, validateFix, validateRoleValue, validateSelector, validateSelectorForAttribute, validateTextValue };