@webability/core 1.6.0 → 1.8.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/dom.cjs +45 -29
- package/dist/dom.d.cts +4 -2
- package/dist/dom.d.ts +4 -2
- package/dist/dom.js +45 -29
- package/dist/index.cjs +57 -41
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +57 -41
- package/dist/playwright.cjs +47 -31
- package/dist/playwright.d.cts +1 -1
- package/dist/playwright.d.ts +1 -1
- package/dist/playwright.js +47 -31
- package/dist/{types-CbNyAKbT.d.cts → types-BgiJ7PGs.d.cts} +180 -9
- package/dist/{types-CbNyAKbT.d.ts → types-BgiJ7PGs.d.ts} +180 -9
- package/package.json +2 -2
|
@@ -1,3 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendered-pixel contrast judge for text on a gradient / image / overlapped
|
|
3
|
+
* background — the cases where axe reports `incomplete` and CSS cannot give a
|
|
4
|
+
* background colour.
|
|
5
|
+
*
|
|
6
|
+
* What the user actually sees behind the glyphs IS measurable: hide every
|
|
7
|
+
* glyph, screenshot the text's own boxes, and compare the text colour with
|
|
8
|
+
* the pixels that were behind it.
|
|
9
|
+
*
|
|
10
|
+
* - every pixel fails the threshold -> the text fails wherever it sits: VIOLATION
|
|
11
|
+
* - any pixel passes -> the background is not uniformly bad: DROP
|
|
12
|
+
* (a mixed gradient is not a confident failure, and never a pass we claim)
|
|
13
|
+
*
|
|
14
|
+
* Anything we cannot measure reliably (semi-transparent text, text over
|
|
15
|
+
* <video>/<canvas>/<iframe>, background-clip:text, transformed or clipped
|
|
16
|
+
* boxes, no screenshot support) is dropped, never judged.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface PixelVerdict {
|
|
20
|
+
selector: string;
|
|
21
|
+
fail: boolean;
|
|
22
|
+
/** Best (highest) contrast ratio found anywhere behind the text. */
|
|
23
|
+
bestRatio: number;
|
|
24
|
+
/** Worst (lowest) contrast ratio found anywhere behind the text. */
|
|
25
|
+
worstRatio: number;
|
|
26
|
+
/** The background pixel behind the text that gave `worstRatio`, as rgb(r,g,b). */
|
|
27
|
+
worstBg?: string;
|
|
28
|
+
/** The background pixel behind the text that gave `bestRatio`, as rgb(r,g,b). */
|
|
29
|
+
bestBg?: string;
|
|
30
|
+
threshold: number;
|
|
31
|
+
fg: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* AI judge for the undecided set.
|
|
36
|
+
*
|
|
37
|
+
* The scanner never hands the user a "needs review" bucket. Findings the
|
|
38
|
+
* engines could not decide (axe `incomplete`, a detector's `needsReview`)
|
|
39
|
+
* first go through the deterministic resolvers (rendered-pixel contrast,
|
|
40
|
+
* captions). What is still undecided after that reaches this judge, which
|
|
41
|
+
* turns each item into one of:
|
|
42
|
+
*
|
|
43
|
+
* - `violation` + a concrete fix → promoted into `issues[]`
|
|
44
|
+
* - `pass` + a one-line reason → recorded in `judged[]`, not reported
|
|
45
|
+
*
|
|
46
|
+
* Anything the judge cannot settle — no model configured, malformed output,
|
|
47
|
+
* timeout, a contrast background that cannot be sampled — is DROPPED, exactly
|
|
48
|
+
* as before. The judge never invents a verdict.
|
|
49
|
+
*
|
|
50
|
+
* Deterministic pre-judges run before any model call (declared-decorative
|
|
51
|
+
* images, images named by their link text, CSS backgrounds outside any
|
|
52
|
+
* control or on a control that already has a name, motion that a
|
|
53
|
+
* `prefers-reduced-motion: reduce` rule stops on that element). Contrast items carry a real pixel measurement
|
|
54
|
+
* (best/worst ratio behind the glyphs, AA threshold for the font size) so the
|
|
55
|
+
* verdict states a ratio the scanner measured, not one the model guessed.
|
|
56
|
+
*
|
|
57
|
+
* Models (local, via an OpenAI-compatible chat endpoint — OpenRouter by
|
|
58
|
+
* default): vision `anthropic/claude-haiku-4.5`, text-only
|
|
59
|
+
* `deepseek/deepseek-v4-flash`. Hosted: POST `${apiUrl}/api/judge-incomplete`.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
declare const JUDGE_DEFAULT_TIMEOUT_MS = 20000;
|
|
63
|
+
declare const DEFAULT_JUDGE_BASE_URL = "https://openrouter.ai/api/v1";
|
|
64
|
+
declare const DEFAULT_JUDGE_VISION_MODEL = "anthropic/claude-haiku-4.5";
|
|
65
|
+
declare const DEFAULT_JUDGE_TEXT_MODEL = "deepseek/deepseek-v4-flash";
|
|
66
|
+
type JudgeVerdictKind = 'pass' | 'violation';
|
|
67
|
+
/** One judged finding, as recorded on `ScanResult.judged`. Small on purpose. */
|
|
68
|
+
interface JudgedEntry$1 {
|
|
69
|
+
id: string;
|
|
70
|
+
type: string;
|
|
71
|
+
verdict: JudgeVerdictKind;
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
/** One undecided finding as sent to the model / hosted endpoint. */
|
|
75
|
+
interface JudgeItem {
|
|
76
|
+
id: string;
|
|
77
|
+
type: string;
|
|
78
|
+
wcag: string;
|
|
79
|
+
message: string;
|
|
80
|
+
selector: string;
|
|
81
|
+
/** outerHTML, ≤400 chars */
|
|
82
|
+
html: string;
|
|
83
|
+
/** Computed context: colours/threshold/ratios for contrast, src/alt/link text for images, … */
|
|
84
|
+
context: Record<string, unknown>;
|
|
85
|
+
/** PNG, base64 (no data: prefix). Present only on the vision path. */
|
|
86
|
+
screenshot?: string;
|
|
87
|
+
}
|
|
88
|
+
interface JudgeRequest {
|
|
89
|
+
url: string;
|
|
90
|
+
level: WcagLevel;
|
|
91
|
+
vision: boolean;
|
|
92
|
+
items: JudgeItem[];
|
|
93
|
+
}
|
|
94
|
+
interface JudgeVerdict {
|
|
95
|
+
id: string;
|
|
96
|
+
verdict: JudgeVerdictKind;
|
|
97
|
+
reason?: string;
|
|
98
|
+
fix?: {
|
|
99
|
+
attribute?: string;
|
|
100
|
+
value?: string;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Returns the raw model output (a JSON array string) or an already-parsed `{verdicts}` / array. */
|
|
104
|
+
type JudgeTransport = (req: JudgeRequest, signal: AbortSignal) => Promise<unknown>;
|
|
105
|
+
interface JudgeOptions {
|
|
106
|
+
/** OpenAI-compatible chat-completions key (OpenRouter). Enables the local path. */
|
|
107
|
+
apiKey?: string;
|
|
108
|
+
/** Base URL of the chat-completions API. Default https://openrouter.ai/api/v1 */
|
|
109
|
+
baseUrl?: string;
|
|
110
|
+
visionModel?: string;
|
|
111
|
+
textModel?: string;
|
|
112
|
+
/** Hosted path: WebAbility API origin; POST `${apiUrl}/api/judge-incomplete`. */
|
|
113
|
+
apiUrl?: string;
|
|
114
|
+
/** Bearer token for the hosted path. */
|
|
115
|
+
apiToken?: string;
|
|
116
|
+
/** Total judge budget per scan (all batches). Default 20 000 ms. */
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
/** Conformance ceiling: only criteria at or below this level are promoted. Default AA. */
|
|
119
|
+
level?: WcagLevel;
|
|
120
|
+
/** false = HTML/text only (scan_html, DOM path). Default: screenshots when the page can take them. */
|
|
121
|
+
vision?: boolean;
|
|
122
|
+
/** Test / custom model injection. Wins over apiKey/apiUrl. */
|
|
123
|
+
transport?: JudgeTransport;
|
|
124
|
+
}
|
|
125
|
+
interface JudgeRunOptions extends JudgeOptions {
|
|
126
|
+
url: string;
|
|
127
|
+
/** Pixel measurements already taken by the contrast resolver (by selector). */
|
|
128
|
+
measured?: Map<string, PixelVerdict>;
|
|
129
|
+
}
|
|
130
|
+
interface JudgeOutcome {
|
|
131
|
+
/** Verdict=violation, now confirmed findings with a fix. Not yet deduped against `issues`. */
|
|
132
|
+
promoted: ScanIssue[];
|
|
133
|
+
/** Every verdict reached (pass and violation), id = the original finding id. */
|
|
134
|
+
judged: JudgedEntry$1[];
|
|
135
|
+
}
|
|
136
|
+
declare function resetJudgeCache(): void;
|
|
137
|
+
/** OpenAI-compatible chat completions (OpenRouter). Vision → image_url parts; text-only → text model. */
|
|
138
|
+
declare function openAiCompatibleTransport(cfg: {
|
|
139
|
+
apiKey: string;
|
|
140
|
+
baseUrl?: string;
|
|
141
|
+
visionModel?: string;
|
|
142
|
+
textModel?: string;
|
|
143
|
+
}): JudgeTransport;
|
|
144
|
+
/**
|
|
145
|
+
* Hosted path: the platform judges on the server. Request body = JudgeRequest;
|
|
146
|
+
* response = `{ verdicts: JudgeVerdict[] }`. Any non-2xx → items stay dropped.
|
|
147
|
+
*/
|
|
148
|
+
declare function hostedJudgeTransport(cfg: {
|
|
149
|
+
apiUrl: string;
|
|
150
|
+
token?: string;
|
|
151
|
+
}): JudgeTransport;
|
|
152
|
+
declare function judgeUndecided(page: PageLike | null, undecided: readonly ScanIssue[], opts: JudgeRunOptions): Promise<JudgeOutcome>;
|
|
153
|
+
|
|
1
154
|
/**
|
|
2
155
|
* @webability/core — type definitions
|
|
3
156
|
*/
|
|
@@ -113,12 +266,10 @@ type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
|
|
|
113
266
|
type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
|
|
114
267
|
type WcagLevel = 'A' | 'AA' | 'AAA';
|
|
115
268
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* to `needs_review` findings — they belong in the `incomplete[]` bucket and
|
|
121
|
-
* require human judgment.
|
|
269
|
+
* Internal confidence marker. `needs_review` means an engine could not decide
|
|
270
|
+
* (text on a gradient, marketing imagery that may be a logo, …). Such findings
|
|
271
|
+
* never reach the result: the pixel/caption resolvers and the AI judge turn
|
|
272
|
+
* them into a verdict, or they are dropped. No reported issue carries it.
|
|
122
273
|
*/
|
|
123
274
|
type IssueConfidence = 'high' | 'needs_review';
|
|
124
275
|
interface ScanIssue {
|
|
@@ -131,9 +282,9 @@ interface ScanIssue {
|
|
|
131
282
|
message: string;
|
|
132
283
|
selector: string;
|
|
133
284
|
html?: string;
|
|
134
|
-
/**
|
|
285
|
+
/** Internal: set by an engine that could not decide; stripped before the result. */
|
|
135
286
|
confidence?: IssueConfidence;
|
|
136
|
-
/**
|
|
287
|
+
/** Internal: why the engine could not decide; handed to the judge as context, never reported. */
|
|
137
288
|
reviewReason?: string;
|
|
138
289
|
fix?: {
|
|
139
290
|
attribute: string;
|
|
@@ -160,6 +311,13 @@ interface BlockedInfo {
|
|
|
160
311
|
/** The specific signal that matched (page title, marker selector, or status). */
|
|
161
312
|
signal?: string;
|
|
162
313
|
}
|
|
314
|
+
/** One judged finding — see `ScanResult.judged`. */
|
|
315
|
+
interface JudgedEntry {
|
|
316
|
+
id: string;
|
|
317
|
+
type: string;
|
|
318
|
+
verdict: 'pass' | 'violation';
|
|
319
|
+
reason: string;
|
|
320
|
+
}
|
|
163
321
|
interface ScanResult {
|
|
164
322
|
url: string;
|
|
165
323
|
timestamp: string;
|
|
@@ -192,6 +350,12 @@ interface ScanResult {
|
|
|
192
350
|
* existing consumers that read it keep compiling.
|
|
193
351
|
*/
|
|
194
352
|
incomplete: ScanIssue[];
|
|
353
|
+
/**
|
|
354
|
+
* What the AI judge decided about findings the engines could not: every
|
|
355
|
+
* verdict (`pass` = cleared with a reason, `violation` = promoted into
|
|
356
|
+
* `issues`). Absent when the judge ran on nothing or is not configured.
|
|
357
|
+
*/
|
|
358
|
+
judged?: JudgedEntry[];
|
|
195
359
|
summary: {
|
|
196
360
|
total: number;
|
|
197
361
|
critical: number;
|
|
@@ -252,6 +416,13 @@ interface ScanOptions {
|
|
|
252
416
|
* uniformly across both entrypoints. Ignored on the URL-string path.
|
|
253
417
|
*/
|
|
254
418
|
mainStatus?: number;
|
|
419
|
+
/**
|
|
420
|
+
* AI judge for findings the engines could not decide. With a model key
|
|
421
|
+
* (local, OpenRouter-compatible) or `apiUrl` (hosted), each such finding
|
|
422
|
+
* becomes a verdict: a violation with a fix in `issues`, or a recorded pass
|
|
423
|
+
* in `judged`. Unset / empty = judge skipped; undecided findings are dropped.
|
|
424
|
+
*/
|
|
425
|
+
judge?: JudgeOptions;
|
|
255
426
|
}
|
|
256
427
|
|
|
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
|
|
428
|
+
export { type BrowserDetector as B, type DetectedIssue as D, type FixDetector as F, type IssueImpact as I, type JudgeOptions as J, 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, DEFAULT_JUDGE_BASE_URL as d, DEFAULT_JUDGE_TEXT_MODEL as e, DEFAULT_JUDGE_VISION_MODEL as f, type FixIssueType as g, type IssueSource as h, JUDGE_DEFAULT_TIMEOUT_MS as i, type JudgeItem as j, type JudgeRequest as k, type JudgeTransport as l, type JudgeVerdict as m, type JudgedEntry as n, type ViewportPreset as o, WCAG_BY_ISSUE as p, hostedJudgeTransport as q, judgeUndecided as r, openAiCompatibleTransport as s, resetJudgeCache as t };
|
|
@@ -1,3 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendered-pixel contrast judge for text on a gradient / image / overlapped
|
|
3
|
+
* background — the cases where axe reports `incomplete` and CSS cannot give a
|
|
4
|
+
* background colour.
|
|
5
|
+
*
|
|
6
|
+
* What the user actually sees behind the glyphs IS measurable: hide every
|
|
7
|
+
* glyph, screenshot the text's own boxes, and compare the text colour with
|
|
8
|
+
* the pixels that were behind it.
|
|
9
|
+
*
|
|
10
|
+
* - every pixel fails the threshold -> the text fails wherever it sits: VIOLATION
|
|
11
|
+
* - any pixel passes -> the background is not uniformly bad: DROP
|
|
12
|
+
* (a mixed gradient is not a confident failure, and never a pass we claim)
|
|
13
|
+
*
|
|
14
|
+
* Anything we cannot measure reliably (semi-transparent text, text over
|
|
15
|
+
* <video>/<canvas>/<iframe>, background-clip:text, transformed or clipped
|
|
16
|
+
* boxes, no screenshot support) is dropped, never judged.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface PixelVerdict {
|
|
20
|
+
selector: string;
|
|
21
|
+
fail: boolean;
|
|
22
|
+
/** Best (highest) contrast ratio found anywhere behind the text. */
|
|
23
|
+
bestRatio: number;
|
|
24
|
+
/** Worst (lowest) contrast ratio found anywhere behind the text. */
|
|
25
|
+
worstRatio: number;
|
|
26
|
+
/** The background pixel behind the text that gave `worstRatio`, as rgb(r,g,b). */
|
|
27
|
+
worstBg?: string;
|
|
28
|
+
/** The background pixel behind the text that gave `bestRatio`, as rgb(r,g,b). */
|
|
29
|
+
bestBg?: string;
|
|
30
|
+
threshold: number;
|
|
31
|
+
fg: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* AI judge for the undecided set.
|
|
36
|
+
*
|
|
37
|
+
* The scanner never hands the user a "needs review" bucket. Findings the
|
|
38
|
+
* engines could not decide (axe `incomplete`, a detector's `needsReview`)
|
|
39
|
+
* first go through the deterministic resolvers (rendered-pixel contrast,
|
|
40
|
+
* captions). What is still undecided after that reaches this judge, which
|
|
41
|
+
* turns each item into one of:
|
|
42
|
+
*
|
|
43
|
+
* - `violation` + a concrete fix → promoted into `issues[]`
|
|
44
|
+
* - `pass` + a one-line reason → recorded in `judged[]`, not reported
|
|
45
|
+
*
|
|
46
|
+
* Anything the judge cannot settle — no model configured, malformed output,
|
|
47
|
+
* timeout, a contrast background that cannot be sampled — is DROPPED, exactly
|
|
48
|
+
* as before. The judge never invents a verdict.
|
|
49
|
+
*
|
|
50
|
+
* Deterministic pre-judges run before any model call (declared-decorative
|
|
51
|
+
* images, images named by their link text, CSS backgrounds outside any
|
|
52
|
+
* control or on a control that already has a name, motion that a
|
|
53
|
+
* `prefers-reduced-motion: reduce` rule stops on that element). Contrast items carry a real pixel measurement
|
|
54
|
+
* (best/worst ratio behind the glyphs, AA threshold for the font size) so the
|
|
55
|
+
* verdict states a ratio the scanner measured, not one the model guessed.
|
|
56
|
+
*
|
|
57
|
+
* Models (local, via an OpenAI-compatible chat endpoint — OpenRouter by
|
|
58
|
+
* default): vision `anthropic/claude-haiku-4.5`, text-only
|
|
59
|
+
* `deepseek/deepseek-v4-flash`. Hosted: POST `${apiUrl}/api/judge-incomplete`.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
declare const JUDGE_DEFAULT_TIMEOUT_MS = 20000;
|
|
63
|
+
declare const DEFAULT_JUDGE_BASE_URL = "https://openrouter.ai/api/v1";
|
|
64
|
+
declare const DEFAULT_JUDGE_VISION_MODEL = "anthropic/claude-haiku-4.5";
|
|
65
|
+
declare const DEFAULT_JUDGE_TEXT_MODEL = "deepseek/deepseek-v4-flash";
|
|
66
|
+
type JudgeVerdictKind = 'pass' | 'violation';
|
|
67
|
+
/** One judged finding, as recorded on `ScanResult.judged`. Small on purpose. */
|
|
68
|
+
interface JudgedEntry$1 {
|
|
69
|
+
id: string;
|
|
70
|
+
type: string;
|
|
71
|
+
verdict: JudgeVerdictKind;
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
/** One undecided finding as sent to the model / hosted endpoint. */
|
|
75
|
+
interface JudgeItem {
|
|
76
|
+
id: string;
|
|
77
|
+
type: string;
|
|
78
|
+
wcag: string;
|
|
79
|
+
message: string;
|
|
80
|
+
selector: string;
|
|
81
|
+
/** outerHTML, ≤400 chars */
|
|
82
|
+
html: string;
|
|
83
|
+
/** Computed context: colours/threshold/ratios for contrast, src/alt/link text for images, … */
|
|
84
|
+
context: Record<string, unknown>;
|
|
85
|
+
/** PNG, base64 (no data: prefix). Present only on the vision path. */
|
|
86
|
+
screenshot?: string;
|
|
87
|
+
}
|
|
88
|
+
interface JudgeRequest {
|
|
89
|
+
url: string;
|
|
90
|
+
level: WcagLevel;
|
|
91
|
+
vision: boolean;
|
|
92
|
+
items: JudgeItem[];
|
|
93
|
+
}
|
|
94
|
+
interface JudgeVerdict {
|
|
95
|
+
id: string;
|
|
96
|
+
verdict: JudgeVerdictKind;
|
|
97
|
+
reason?: string;
|
|
98
|
+
fix?: {
|
|
99
|
+
attribute?: string;
|
|
100
|
+
value?: string;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Returns the raw model output (a JSON array string) or an already-parsed `{verdicts}` / array. */
|
|
104
|
+
type JudgeTransport = (req: JudgeRequest, signal: AbortSignal) => Promise<unknown>;
|
|
105
|
+
interface JudgeOptions {
|
|
106
|
+
/** OpenAI-compatible chat-completions key (OpenRouter). Enables the local path. */
|
|
107
|
+
apiKey?: string;
|
|
108
|
+
/** Base URL of the chat-completions API. Default https://openrouter.ai/api/v1 */
|
|
109
|
+
baseUrl?: string;
|
|
110
|
+
visionModel?: string;
|
|
111
|
+
textModel?: string;
|
|
112
|
+
/** Hosted path: WebAbility API origin; POST `${apiUrl}/api/judge-incomplete`. */
|
|
113
|
+
apiUrl?: string;
|
|
114
|
+
/** Bearer token for the hosted path. */
|
|
115
|
+
apiToken?: string;
|
|
116
|
+
/** Total judge budget per scan (all batches). Default 20 000 ms. */
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
/** Conformance ceiling: only criteria at or below this level are promoted. Default AA. */
|
|
119
|
+
level?: WcagLevel;
|
|
120
|
+
/** false = HTML/text only (scan_html, DOM path). Default: screenshots when the page can take them. */
|
|
121
|
+
vision?: boolean;
|
|
122
|
+
/** Test / custom model injection. Wins over apiKey/apiUrl. */
|
|
123
|
+
transport?: JudgeTransport;
|
|
124
|
+
}
|
|
125
|
+
interface JudgeRunOptions extends JudgeOptions {
|
|
126
|
+
url: string;
|
|
127
|
+
/** Pixel measurements already taken by the contrast resolver (by selector). */
|
|
128
|
+
measured?: Map<string, PixelVerdict>;
|
|
129
|
+
}
|
|
130
|
+
interface JudgeOutcome {
|
|
131
|
+
/** Verdict=violation, now confirmed findings with a fix. Not yet deduped against `issues`. */
|
|
132
|
+
promoted: ScanIssue[];
|
|
133
|
+
/** Every verdict reached (pass and violation), id = the original finding id. */
|
|
134
|
+
judged: JudgedEntry$1[];
|
|
135
|
+
}
|
|
136
|
+
declare function resetJudgeCache(): void;
|
|
137
|
+
/** OpenAI-compatible chat completions (OpenRouter). Vision → image_url parts; text-only → text model. */
|
|
138
|
+
declare function openAiCompatibleTransport(cfg: {
|
|
139
|
+
apiKey: string;
|
|
140
|
+
baseUrl?: string;
|
|
141
|
+
visionModel?: string;
|
|
142
|
+
textModel?: string;
|
|
143
|
+
}): JudgeTransport;
|
|
144
|
+
/**
|
|
145
|
+
* Hosted path: the platform judges on the server. Request body = JudgeRequest;
|
|
146
|
+
* response = `{ verdicts: JudgeVerdict[] }`. Any non-2xx → items stay dropped.
|
|
147
|
+
*/
|
|
148
|
+
declare function hostedJudgeTransport(cfg: {
|
|
149
|
+
apiUrl: string;
|
|
150
|
+
token?: string;
|
|
151
|
+
}): JudgeTransport;
|
|
152
|
+
declare function judgeUndecided(page: PageLike | null, undecided: readonly ScanIssue[], opts: JudgeRunOptions): Promise<JudgeOutcome>;
|
|
153
|
+
|
|
1
154
|
/**
|
|
2
155
|
* @webability/core — type definitions
|
|
3
156
|
*/
|
|
@@ -113,12 +266,10 @@ type IssueSource = 'webability' | 'axe-core' | 'deep-analysis';
|
|
|
113
266
|
type IssueImpact = 'critical' | 'serious' | 'moderate' | 'minor';
|
|
114
267
|
type WcagLevel = 'A' | 'AA' | 'AAA';
|
|
115
268
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* to `needs_review` findings — they belong in the `incomplete[]` bucket and
|
|
121
|
-
* require human judgment.
|
|
269
|
+
* Internal confidence marker. `needs_review` means an engine could not decide
|
|
270
|
+
* (text on a gradient, marketing imagery that may be a logo, …). Such findings
|
|
271
|
+
* never reach the result: the pixel/caption resolvers and the AI judge turn
|
|
272
|
+
* them into a verdict, or they are dropped. No reported issue carries it.
|
|
122
273
|
*/
|
|
123
274
|
type IssueConfidence = 'high' | 'needs_review';
|
|
124
275
|
interface ScanIssue {
|
|
@@ -131,9 +282,9 @@ interface ScanIssue {
|
|
|
131
282
|
message: string;
|
|
132
283
|
selector: string;
|
|
133
284
|
html?: string;
|
|
134
|
-
/**
|
|
285
|
+
/** Internal: set by an engine that could not decide; stripped before the result. */
|
|
135
286
|
confidence?: IssueConfidence;
|
|
136
|
-
/**
|
|
287
|
+
/** Internal: why the engine could not decide; handed to the judge as context, never reported. */
|
|
137
288
|
reviewReason?: string;
|
|
138
289
|
fix?: {
|
|
139
290
|
attribute: string;
|
|
@@ -160,6 +311,13 @@ interface BlockedInfo {
|
|
|
160
311
|
/** The specific signal that matched (page title, marker selector, or status). */
|
|
161
312
|
signal?: string;
|
|
162
313
|
}
|
|
314
|
+
/** One judged finding — see `ScanResult.judged`. */
|
|
315
|
+
interface JudgedEntry {
|
|
316
|
+
id: string;
|
|
317
|
+
type: string;
|
|
318
|
+
verdict: 'pass' | 'violation';
|
|
319
|
+
reason: string;
|
|
320
|
+
}
|
|
163
321
|
interface ScanResult {
|
|
164
322
|
url: string;
|
|
165
323
|
timestamp: string;
|
|
@@ -192,6 +350,12 @@ interface ScanResult {
|
|
|
192
350
|
* existing consumers that read it keep compiling.
|
|
193
351
|
*/
|
|
194
352
|
incomplete: ScanIssue[];
|
|
353
|
+
/**
|
|
354
|
+
* What the AI judge decided about findings the engines could not: every
|
|
355
|
+
* verdict (`pass` = cleared with a reason, `violation` = promoted into
|
|
356
|
+
* `issues`). Absent when the judge ran on nothing or is not configured.
|
|
357
|
+
*/
|
|
358
|
+
judged?: JudgedEntry[];
|
|
195
359
|
summary: {
|
|
196
360
|
total: number;
|
|
197
361
|
critical: number;
|
|
@@ -252,6 +416,13 @@ interface ScanOptions {
|
|
|
252
416
|
* uniformly across both entrypoints. Ignored on the URL-string path.
|
|
253
417
|
*/
|
|
254
418
|
mainStatus?: number;
|
|
419
|
+
/**
|
|
420
|
+
* AI judge for findings the engines could not decide. With a model key
|
|
421
|
+
* (local, OpenRouter-compatible) or `apiUrl` (hosted), each such finding
|
|
422
|
+
* becomes a verdict: a violation with a fix in `issues`, or a recorded pass
|
|
423
|
+
* in `judged`. Unset / empty = judge skipped; undecided findings are dropped.
|
|
424
|
+
*/
|
|
425
|
+
judge?: JudgeOptions;
|
|
255
426
|
}
|
|
256
427
|
|
|
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
|
|
428
|
+
export { type BrowserDetector as B, type DetectedIssue as D, type FixDetector as F, type IssueImpact as I, type JudgeOptions as J, 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, DEFAULT_JUDGE_BASE_URL as d, DEFAULT_JUDGE_TEXT_MODEL as e, DEFAULT_JUDGE_VISION_MODEL as f, type FixIssueType as g, type IssueSource as h, JUDGE_DEFAULT_TIMEOUT_MS as i, type JudgeItem as j, type JudgeRequest as k, type JudgeTransport as l, type JudgeVerdict as m, type JudgedEntry as n, type ViewportPreset as o, WCAG_BY_ISSUE as p, hostedJudgeTransport as q, judgeUndecided as r, openAiCompatibleTransport as s, resetJudgeCache as t };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webability/core",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Accessibility scanner engine
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "Accessibility scanner engine — WCAG 2.2 AA compliance testing with 200+ rules",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|