ultra11y 2.22.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/LICENSE +21 -0
- package/README.md +223 -0
- package/docker/Dockerfile +7 -0
- package/docker/package.json +9 -0
- package/docker/runner.mjs +23 -0
- package/package.json +73 -0
- package/scripts/bench.mjs +151 -0
- package/scripts/build-act-corpus.mjs +143 -0
- package/scripts/build-adjudication.mjs +503 -0
- package/scripts/build-pack-rgaa.mjs +397 -0
- package/scripts/build-standards.mjs +548 -0
- package/scripts/copy-bundle.mjs +39 -0
- package/scripts/import-pack.mjs +107 -0
- package/scripts/sync-engine.mjs +84 -0
- package/scripts/sync-version.mjs +43 -0
- package/scripts/ultra11y.mjs +52739 -0
- package/scripts/vendor/act-testcases.json +12297 -0
- package/scripts/vendor/rgaa/SOURCE.json +8 -0
- package/scripts/vendor/rgaa/criteres.json +4144 -0
- package/scripts/vendor/rgaa/glossaire.json +480 -0
- package/scripts/vendor/wcag-2.2-fr.json +82 -0
- package/scripts/vendor/wcag-2.2-sc-universe.json +950 -0
- package/scripts/vendor/wcag-2.2-sc.json +574 -0
- package/scripts/verify-rgaa-source.mjs +126 -0
- package/scripts/verify-skill-bundle.mjs +91 -0
- package/skills/review-a11y/SKILL.md +123 -0
- package/skills/review-a11y/scripts/ultra11y.mjs +52739 -0
- package/skills/ultra11y/SKILL.md +292 -0
- package/skills/ultra11y/references/act.md +163 -0
- package/skills/ultra11y/references/adjudication.md +599 -0
- package/skills/ultra11y/references/audit.md +100 -0
- package/skills/ultra11y/references/authoring.md +37 -0
- package/skills/ultra11y/references/automation.md +113 -0
- package/skills/ultra11y/references/correction.md +41 -0
- package/skills/ultra11y/references/criteria.md +129 -0
- package/skills/ultra11y/references/cross-file.md +82 -0
- package/skills/ultra11y/references/dynamic.md +152 -0
- package/skills/ultra11y/references/false-positives.md +81 -0
- package/skills/ultra11y/references/fix.md +46 -0
- package/skills/ultra11y/references/focus-and-logic.md +64 -0
- package/skills/ultra11y/references/forbidden-patterns.md +61 -0
- package/skills/ultra11y/references/forms-and-errors.md +147 -0
- package/skills/ultra11y/references/guidance.md +55 -0
- package/skills/ultra11y/references/judgment.md +90 -0
- package/skills/ultra11y/references/media-and-motion.md +116 -0
- package/skills/ultra11y/references/methodology.md +72 -0
- package/skills/ultra11y/references/naming.md +117 -0
- package/skills/ultra11y/references/orchestration.md +44 -0
- package/skills/ultra11y/references/packs.md +193 -0
- package/skills/ultra11y/references/prd.md +104 -0
- package/skills/ultra11y/references/rendered.md +110 -0
- package/skills/ultra11y/references/scale.md +49 -0
- package/skills/ultra11y/references/standards.md +97 -0
- package/skills/ultra11y/references/structure.md +138 -0
- package/skills/ultra11y/references/verify.md +88 -0
- package/skills/ultra11y/references/widgets.md +106 -0
- package/skills/ultra11y/scripts/ultra11y.mjs +52739 -0
- package/src/act.ts +441 -0
- package/src/adjudicate.ts +630 -0
- package/src/aria.ts +230 -0
- package/src/audit.ts +396 -0
- package/src/auditor.ts +399 -0
- package/src/autofill.ts +102 -0
- package/src/axe-map.ts +294 -0
- package/src/baseline.ts +75 -0
- package/src/capture.ts +182 -0
- package/src/check.ts +243 -0
- package/src/cli.ts +1927 -0
- package/src/color.ts +102 -0
- package/src/config.ts +182 -0
- package/src/crawl.ts +75 -0
- package/src/criteria.ts +183 -0
- package/src/data/adjudication.json +770 -0
- package/src/data/guidance/rgaa.json +2395 -0
- package/src/data/standards/rgaa.glossary.json +478 -0
- package/src/data/standards/rgaa.json +3454 -0
- package/src/data/wcag-universe.json +530 -0
- package/src/data/wcag.json +1178 -0
- package/src/discover.ts +203 -0
- package/src/fix/codemods.ts +76 -0
- package/src/fix/diff.ts +64 -0
- package/src/fix/edits.ts +126 -0
- package/src/fix.ts +215 -0
- package/src/gh.ts +214 -0
- package/src/glob.ts +206 -0
- package/src/graph/build.ts +114 -0
- package/src/graph/engine-resolve.ts +0 -0
- package/src/graph/graph.ts +126 -0
- package/src/graph/imports.ts +359 -0
- package/src/graph/resolve.ts +73 -0
- package/src/graph/tsconfig.ts +113 -0
- package/src/grounding.ts +119 -0
- package/src/guidance/index.ts +45 -0
- package/src/guidance/types.ts +36 -0
- package/src/init.ts +113 -0
- package/src/mcp/handlers.ts +388 -0
- package/src/mcp/http.ts +260 -0
- package/src/mcp/prompts.ts +183 -0
- package/src/mcp/protocol.ts +187 -0
- package/src/mcp/resources.ts +136 -0
- package/src/mcp/server.ts +228 -0
- package/src/mcp/stdio.ts +119 -0
- package/src/mcp/tools.ts +347 -0
- package/src/messages.ts +1011 -0
- package/src/name.ts +174 -0
- package/src/orchestrate-templates.ts +231 -0
- package/src/orchestrate.ts +186 -0
- package/src/output.ts +108 -0
- package/src/pack.ts +187 -0
- package/src/parse/html.ts +258 -0
- package/src/parse/jsx-ast.ts +67 -0
- package/src/parse/jsx-bridge.ts +247 -0
- package/src/parse/jsx.ts +19 -0
- package/src/parse/source.ts +75 -0
- package/src/prd.ts +406 -0
- package/src/project-lock.ts +42 -0
- package/src/render.ts +307 -0
- package/src/report.ts +399 -0
- package/src/rules/colors.ts +125 -0
- package/src/rules/cross-registry.ts +264 -0
- package/src/rules/cross-rule.ts +83 -0
- package/src/rules/forms.ts +590 -0
- package/src/rules/frames.ts +33 -0
- package/src/rules/headings.ts +227 -0
- package/src/rules/images.ts +200 -0
- package/src/rules/links.ts +193 -0
- package/src/rules/mandatory.ts +173 -0
- package/src/rules/multimedia.ts +77 -0
- package/src/rules/navigation.ts +190 -0
- package/src/rules/presentation.ts +75 -0
- package/src/rules/registry.ts +57 -0
- package/src/rules/rule.ts +122 -0
- package/src/rules/scripts-aria.ts +636 -0
- package/src/rules/tables.ts +264 -0
- package/src/rules/timing.ts +55 -0
- package/src/sample.ts +176 -0
- package/src/scan-local.ts +866 -0
- package/src/scan.ts +538 -0
- package/src/standards/derive.ts +195 -0
- package/src/standards/index.ts +31 -0
- package/src/standards/pack-rules.ts +111 -0
- package/src/standards/pack.ts +81 -0
- package/src/standards/registry.ts +100 -0
- package/src/standards/types.ts +202 -0
- package/src/standards/validate.ts +544 -0
- package/src/standards/vocabulary.ts +75 -0
- package/src/types.ts +355 -0
- package/src/util.ts +32 -0
- package/src/vendor/codeindex-engine.d.mts +1009 -0
- package/src/vendor/codeindex-engine.mjs +13701 -0
- package/src/vendor/engine.meta.json +9 -0
- package/src/verify.ts +275 -0
- package/src/wcag.ts +128 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// Single source of truth for shared types + the embedded version. sync-version.mjs
|
|
2
|
+
// rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
|
|
3
|
+
export const VERSION = "2.22.0";
|
|
4
|
+
export const SCHEMA_VERSION = 2;
|
|
5
|
+
|
|
6
|
+
// Lang = the UI FRAME's language — the `L` localization tables each module (report,
|
|
7
|
+
// check, verify, criteria…) renders in. NOT a country pack's own locale set, which may
|
|
8
|
+
// carry any BCP-47-ish tag ("de", "pt-BR"…) — see `LocaleString` in
|
|
9
|
+
// src/standards/types.ts for that decoupled, per-pack concept.
|
|
10
|
+
export type Lang = "fr" | "en";
|
|
11
|
+
export type Severity = "bloquant" | "majeur" | "mineur";
|
|
12
|
+
// C = conforme, NC = non conforme, NA = non applicable, manual = à évaluer
|
|
13
|
+
// (needs-rendering / judgment criteria the engine cannot decide on its own).
|
|
14
|
+
export type Status = "C" | "NC" | "NA" | "manual";
|
|
15
|
+
export type Automatability = "static" | "needs-rendering" | "judgment";
|
|
16
|
+
export type ParserKind = "html" | "css" | "jsx" | "cross";
|
|
17
|
+
|
|
18
|
+
export interface GlossaryEntry {
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
}
|
|
22
|
+
export type Glossary = Record<string, GlossaryEntry>;
|
|
23
|
+
|
|
24
|
+
// ---- normative page sample (échantillon) — Task 5. STANDARD-AGNOSTIC mechanics: a real
|
|
25
|
+
// audit runs over a representative set of served pages (+ transverse elements audited on
|
|
26
|
+
// every page), NOT the whole file tree. The REQUIRED page KINDS a given standard mandates
|
|
27
|
+
// live in the pack (`sampleMethodology`, src/standards/types.ts), never here.
|
|
28
|
+
// SECURITY: `storageState` is a FILE PATH (a Playwright session file) — its CONTENT is
|
|
29
|
+
// never read into any output/report/finding; only the path is ever cited.
|
|
30
|
+
export interface SamplePage {
|
|
31
|
+
id: string; // stable slug used for per-page provenance, e.g. "accueil"
|
|
32
|
+
name: string; // human page name shown in the report, e.g. "Page d'accueil"
|
|
33
|
+
url: string; // served URL (http(s)://) or a local HTML file path
|
|
34
|
+
auth?: boolean; // the page sits behind authentication (renders an auth badge)
|
|
35
|
+
storageState?: string; // Playwright storageState FILE PATH (content never read) — per-page auth
|
|
36
|
+
notes?: string; // reproduction steps / required state, surfaced in the ticket repro block
|
|
37
|
+
}
|
|
38
|
+
export interface SampleConfig {
|
|
39
|
+
pages: SamplePage[];
|
|
40
|
+
// Element descriptions audited on EVERY page (header, nav, footer, modals…).
|
|
41
|
+
transverse?: string[];
|
|
42
|
+
}
|
|
43
|
+
// The sample as recorded in an AuditResult / DynamicResult. The `storageState` PATH is
|
|
44
|
+
// deliberately dropped (never persisted): the report needs name/url/auth/notes only.
|
|
45
|
+
export interface SampleScope {
|
|
46
|
+
pages: Array<{ id: string; name: string; url: string; auth?: boolean; notes?: string }>;
|
|
47
|
+
transverse?: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---- WCAG 2.2 canonical core (src/data/wcag.json, produced by scripts/build-standards.mjs)
|
|
51
|
+
// The engine's canonical key. Success-criterion ids/titles/levels are derived from the
|
|
52
|
+
// official W3C source (https://github.com/w3c/wcag); rule coverage + automatability are
|
|
53
|
+
// engine-specific. RGAA and other country standards are derived packs (see src/standards/).
|
|
54
|
+
export type WcagLevel = "A" | "AA";
|
|
55
|
+
|
|
56
|
+
export interface Sc {
|
|
57
|
+
sc: string; // dotted SC id, "<principle>.<guideline>.<n>", e.g. "1.4.3"
|
|
58
|
+
principle: number; // 1..4
|
|
59
|
+
guideline: string; // "1.4"
|
|
60
|
+
title: string; // authoritative W3C English title
|
|
61
|
+
// Authoritative French title from the W3C AUTHORIZED translation
|
|
62
|
+
// (https://www.w3.org/Translations/WCAG22-fr/), vendored at scripts/vendor/wcag-2.2-fr.json.
|
|
63
|
+
// Present on every core-AA SC (the build fails otherwise) — optional only so older
|
|
64
|
+
// wcag.json snapshots still parse. See src/wcag.ts `scTitle`.
|
|
65
|
+
titleFr?: string;
|
|
66
|
+
level: WcagLevel;
|
|
67
|
+
addedIn: string; // "2.0" | "2.1" | "2.2"
|
|
68
|
+
automatability: Automatability;
|
|
69
|
+
ruleIds: string[]; // engine rules contributing to this SC
|
|
70
|
+
understanding: string; // W3C Understanding doc URL (manual-check grounding)
|
|
71
|
+
techniques?: string[]; // language-neutral W3C technique codes
|
|
72
|
+
tests?: string[]; // optional manual-check lines
|
|
73
|
+
notes?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface WcagPrinciple {
|
|
77
|
+
number: number;
|
|
78
|
+
title: string;
|
|
79
|
+
titleFr?: string; // W3C authorized French translation — see Sc.titleFr
|
|
80
|
+
}
|
|
81
|
+
export interface WcagGuideline {
|
|
82
|
+
number: string; // "1.4"
|
|
83
|
+
title: string;
|
|
84
|
+
titleFr?: string; // W3C authorized French translation — see Sc.titleFr
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface WcagData {
|
|
88
|
+
wcagVersion: string; // "2.2"
|
|
89
|
+
level: string; // "AA"
|
|
90
|
+
source: string;
|
|
91
|
+
license: string;
|
|
92
|
+
criteriaSource: string;
|
|
93
|
+
principles: WcagPrinciple[];
|
|
94
|
+
guidelines: WcagGuideline[];
|
|
95
|
+
criteria: Sc[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---- WCAG SC universe (src/data/wcag-universe.json, produced by
|
|
99
|
+
// scripts/build-standards.mjs --refresh-universe + build()): EVERY real WCAG 2.x
|
|
100
|
+
// success criterion — every level (A/AA/AAA), plus the obsolete/removed 4.1.1 Parsing —
|
|
101
|
+
// derived from the same vendored W3C source as the AA core, never invented. This is the
|
|
102
|
+
// guardrail a pack's out-of-core SC mapping (e.g. an AAA criterion, or the removed 4.1.1)
|
|
103
|
+
// is checked against, so validation never has to hardcode a single tolerated exception —
|
|
104
|
+
// see src/wcag.ts `knownScStatus` and src/standards/validate.ts `classifySc`.
|
|
105
|
+
export type ScStatus = "core-AA" | "out-of-core" | "removed";
|
|
106
|
+
|
|
107
|
+
export interface WcagUniverseEntry {
|
|
108
|
+
id: string; // dotted SC id, e.g. "1.4.6"
|
|
109
|
+
title: string;
|
|
110
|
+
level: string; // "A" | "AA" | "AAA" | "" (no level ⇒ removed/obsolete)
|
|
111
|
+
status: ScStatus;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface WcagUniverseData {
|
|
115
|
+
wcagVersion: string;
|
|
116
|
+
source: string;
|
|
117
|
+
criteriaSource: string;
|
|
118
|
+
provenance: string;
|
|
119
|
+
criteria: WcagUniverseEntry[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---- engine findings + audit result
|
|
123
|
+
export interface Finding {
|
|
124
|
+
ruleId: string;
|
|
125
|
+
criteriaId: string;
|
|
126
|
+
file: string;
|
|
127
|
+
line: number;
|
|
128
|
+
col: number;
|
|
129
|
+
selectorHint: string;
|
|
130
|
+
severity: Severity;
|
|
131
|
+
message: string;
|
|
132
|
+
remediation: string;
|
|
133
|
+
// Language-neutral resolution key (src/messages.ts MSG_CATALOG), additive/optional so
|
|
134
|
+
// older AuditResult JSON (no `msg`) still renders via the baked message/remediation
|
|
135
|
+
// above. `message`/`remediation` are the canonical ENGLISH bake (AI-facing); a renderer
|
|
136
|
+
// resolves `msg` through resolveMessage/resolveRemediation for `--lang fr` (or any other
|
|
137
|
+
// supported lang), falling back to the baked strings when absent or the id is unknown.
|
|
138
|
+
msg?: { id: string; params?: Record<string, string | number> };
|
|
139
|
+
snippet: string;
|
|
140
|
+
// Source byte range of the anchoring element (htmlparser2 [start, end), open+close
|
|
141
|
+
// tag). Optional so older AuditResult JSON still parses. Used by `fix` (codemods
|
|
142
|
+
// edit by range) and by `init` baseline diffing (stable finding identity that
|
|
143
|
+
// survives line drift). Absent for stdin/JSX findings where it would be unusable.
|
|
144
|
+
sourceStart?: number;
|
|
145
|
+
sourceEnd?: number;
|
|
146
|
+
// Cross-file context: the OTHER site that explains this finding — e.g. the
|
|
147
|
+
// component definition for a finding raised at a usage site. Optional/additive
|
|
148
|
+
// (cross-file rules only), so existing AuditResult JSON still parses.
|
|
149
|
+
// `note` is the canonical baked ENGLISH prose (mirrors message/remediation above);
|
|
150
|
+
// `noteId` is an optional key into src/messages.ts's note catalog, resolved at
|
|
151
|
+
// render time by `resolveNote` for `--lang fr` (or any other supported lang),
|
|
152
|
+
// falling back to the baked `note` when absent or the id is unknown.
|
|
153
|
+
related?: { file: string; line: number; col: number; selectorHint: string; note: string; noteId?: string };
|
|
154
|
+
// AI signal: this finding was raised on a .vue/.svelte/.astro SOURCE template, whose
|
|
155
|
+
// slot/dynamic-injected content is invisible to static analysis — so it is a
|
|
156
|
+
// PRELIMINARY verdict to confirm against the rendered output, not a certainty.
|
|
157
|
+
// Optional/additive (no schemaVersion bump); absent = full-confidence static finding.
|
|
158
|
+
preliminary?: boolean;
|
|
159
|
+
// Set when this finding was raised on a RENDERED capture file (real serialized DOM)
|
|
160
|
+
// and re-attributed to the source component that produced it. `file`/`sourceStart`
|
|
161
|
+
// still index the capture bytes (so `fix` and baseline diffing stay stable); origin
|
|
162
|
+
// points back to the source. Optional/additive (no schemaVersion bump).
|
|
163
|
+
origin?: { capture: string; sourceFile?: string; component?: string; sourceLine?: number };
|
|
164
|
+
// NON-NORMATIVE recommendation: a good-practice signal NOT backed by a testable
|
|
165
|
+
// criterion of the active standard (e.g. "one h1 per page", a best-practice-only axe
|
|
166
|
+
// violation, an agent-noted UX improvement). An advisory finding can NEVER flip a
|
|
167
|
+
// criterion to NC nor enter `conformancePct`; it is rendered as « Recommandation (non
|
|
168
|
+
// normative) », never as a non-conformity, but stays attached to its criterion and in
|
|
169
|
+
// the flat findings list so grounding + `check` still resolve it. Optional/additive
|
|
170
|
+
// (no SCHEMA_VERSION bump) — absent ⇒ normative, following the `preliminary`/`origin`/
|
|
171
|
+
// `decidedBy` pattern.
|
|
172
|
+
advisory?: boolean;
|
|
173
|
+
// Per-sample provenance (`scan --sample`): the crawled page NAME, whether it sits behind
|
|
174
|
+
// authentication, and optional reproduction notes. Attached by `mergeDynamic` from the
|
|
175
|
+
// dynamic finding's originating sample page, so the auditor ticket (src/auditor.ts) can
|
|
176
|
+
// render the human page name + auth flag under « Pages / URLs impactées » and the notes
|
|
177
|
+
// under « Contexte de reproduction ». Optional/additive (no SCHEMA_VERSION bump).
|
|
178
|
+
sample?: { page?: string; authRequired?: boolean; notes?: string };
|
|
179
|
+
// Localized message/remediation PAIR carried on the finding itself — the runtime channel
|
|
180
|
+
// `resolveMessage`/`resolveRemediation` read BEFORE the static MSG_CATALOG. Declarative
|
|
181
|
+
// pack rules (src/standards/pack-rules.ts) are loaded at runtime and cannot register into
|
|
182
|
+
// the compiled MSG_CATALOG, so a `pack:<key>:<id>` finding attaches its rule's `{en,fr}`
|
|
183
|
+
// strings here; a renderer at `--lang fr` then picks the French text instead of falling
|
|
184
|
+
// back to the English `message` bake. Keyed by the UI frame's `Lang` (fr|en). Optional/
|
|
185
|
+
// additive (no SCHEMA_VERSION bump) — absent ⇒ resolve via `msg`/baked strings as before.
|
|
186
|
+
i18n?: { message: Partial<Record<Lang, string>>; remediation: Partial<Record<Lang, string>> };
|
|
187
|
+
// Set when this finding was projected onto a pack criterion via an opt-in SECONDARY
|
|
188
|
+
// crosswalk mapping (src/standards/types.ts SecondaryMapping) — an additional criterion
|
|
189
|
+
// whose official WCAG mapping does NOT contain the finding's SC, an explicit,
|
|
190
|
+
// config-enabled deviation. Carried ONLY on the copy attached to the secondary criterion
|
|
191
|
+
// in a pack projection (`derivePackResults`); the core finding is never mutated. `note`
|
|
192
|
+
// is the resolved localized explanation of the deviation, appended by the auditor block
|
|
193
|
+
// (src/auditor.ts). Optional/additive (no SCHEMA_VERSION bump) — absent ⇒ ordinary
|
|
194
|
+
// SC-faithful projection, following the `advisory`/`preliminary`/`origin` pattern.
|
|
195
|
+
secondary?: { note?: string };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface CriterionResult {
|
|
199
|
+
id: string; // WCAG success-criterion id, e.g. "1.4.3"
|
|
200
|
+
guideline: string; // WCAG guideline this SC belongs to, e.g. "1.4"
|
|
201
|
+
status: Status;
|
|
202
|
+
findings: Finding[];
|
|
203
|
+
justification?: string;
|
|
204
|
+
// Who decided this criterion's status. Absent = the deterministic engine (the default
|
|
205
|
+
// for every audit). "agent" = an AI adjudication of a formerly-`manual` judgment/
|
|
206
|
+
// rendering criterion, recorded via `verify --manual` → `verify --apply` (each carries a
|
|
207
|
+
// justification; an NC carries groundable findings). "scan" = the dynamic tier upgraded
|
|
208
|
+
// a needs-rendering residual to C/NC (src/scan.ts mergeDynamic). Optional/additive — no
|
|
209
|
+
// SCHEMA_VERSION bump; older AuditResult JSON (no `decidedBy`) reads as engine-decided.
|
|
210
|
+
decidedBy?: "engine" | "agent" | "scan";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface GuidelineTally {
|
|
214
|
+
key: string; // WCAG guideline number, e.g. "1.4"
|
|
215
|
+
title: string;
|
|
216
|
+
c: number;
|
|
217
|
+
nc: number;
|
|
218
|
+
na: number;
|
|
219
|
+
manual: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface ResidualRisk {
|
|
223
|
+
criteriaId: string;
|
|
224
|
+
reason: string;
|
|
225
|
+
automatability: Automatability;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface AuditResult {
|
|
229
|
+
tool: "ultra11y";
|
|
230
|
+
standard: "wcag"; // self-describing: the engine keys on WCAG 2.2 SCs (packs are derived views)
|
|
231
|
+
version: string;
|
|
232
|
+
schemaVersion: number;
|
|
233
|
+
date: string; // YYYY-MM-DD
|
|
234
|
+
scope: {
|
|
235
|
+
inputs: string[];
|
|
236
|
+
files: number;
|
|
237
|
+
// Set when `--max-files` capped discovery (highest-priority files audited first).
|
|
238
|
+
truncated?: { limit: number; total: number; skipped: number };
|
|
239
|
+
// Set when content de-duplication collapsed identical files to one canonical audit.
|
|
240
|
+
dedup?: { canonicalFiles: number; duplicateFiles: number };
|
|
241
|
+
// Set when source files render component-LIBRARY components whose real HTML
|
|
242
|
+
// output is invisible to static source analysis (e.g. DSFR). The source verdict
|
|
243
|
+
// is preliminary for them — audit the build output (`render`) or `scan`.
|
|
244
|
+
rendered?: { opaqueLibraries: string[]; files: number };
|
|
245
|
+
// Set when .vue/.svelte/.astro SOURCE templates were audited. Their slots,
|
|
246
|
+
// snippets and dynamic bindings are invisible to static analysis, so findings on
|
|
247
|
+
// them are PRELIMINARY — audit the rendered output (`render`/`scan`) to confirm.
|
|
248
|
+
sourceTemplate?: { files: number; extensions: string[] };
|
|
249
|
+
// Set when RENDERED capture files (real serialized DOM) were audited at full
|
|
250
|
+
// fidelity — the true markup a component library / SFC emits, not its source call.
|
|
251
|
+
captures?: { files: number; components: string[] };
|
|
252
|
+
// Set by the coverage pass (`--require-captures` / `render --coverage`): which
|
|
253
|
+
// components have a rendered capture vs which are still opaque-source-only blind
|
|
254
|
+
// spots. Keys are "posix/path#Component". `unattributed` = capture files with no
|
|
255
|
+
// resolvable source (no provenance) — audited, but not credited to a component.
|
|
256
|
+
captureCoverage?: { total: number; covered: string[]; blindSpots: string[]; unattributed: number };
|
|
257
|
+
// Set when at least one `<html lang>`/`xml:lang` was seen: the repo's declared
|
|
258
|
+
// language(s) (primary BCP-47 subtag, e.g. "fr-FR" → "fr"), sorted by descending
|
|
259
|
+
// frequency. Used as the CLI's `--lang auto` repo-detection signal (see
|
|
260
|
+
// `resolveLang` in src/cli.ts) — never invented when no document declares one.
|
|
261
|
+
langs?: string[];
|
|
262
|
+
// Set when a `scan --sample` run was merged in: the normative page sample the dynamic
|
|
263
|
+
// tier was run over (name/url/auth/notes per page, storageState paths dropped). Drives
|
|
264
|
+
// the report's « Constats par page » section. Optional/additive.
|
|
265
|
+
sample?: SampleScope;
|
|
266
|
+
// Set when dynamic scan results were merged in: which needs-rendering SCs the scan's
|
|
267
|
+
// engines/probes actually MEASURED (verdict coverage — independent of whether anything
|
|
268
|
+
// was found). Docker runner: 320px reflow only; the local runtime adds zoom / text
|
|
269
|
+
// spacing / focus / hover (+ live regions when interactions are on). Union across
|
|
270
|
+
// merges. Drives the partial-audit advisory (src/report.ts untestedNeedsRendering) so
|
|
271
|
+
// the banner never claims a probe ran when it did not. Optional/additive.
|
|
272
|
+
scan?: { testedScs: string[] };
|
|
273
|
+
};
|
|
274
|
+
guidelines: GuidelineTally[];
|
|
275
|
+
criteria: CriterionResult[];
|
|
276
|
+
findings: Finding[];
|
|
277
|
+
// Findings raised by DECLARATIVE PACK RULES (src/standards/pack-rules.ts), namespaced
|
|
278
|
+
// `pack:<key>:<id>`. Kept SEPARATE from the core `findings`/`criteria` so the WCAG core
|
|
279
|
+
// verdict is never touched by pack-only detection: they surface only when a pack
|
|
280
|
+
// projection is derived (`derivePackResults` routes them through the SAME
|
|
281
|
+
// appliesTo/ruleMatches machinery as engine findings). Optional/additive (no
|
|
282
|
+
// SCHEMA_VERSION bump) — absent/empty ⇒ no pack ran a rule.
|
|
283
|
+
packFindings?: Finding[];
|
|
284
|
+
residualRisks: ResidualRisk[];
|
|
285
|
+
// Automatic static-check pass rate over the machine-DECIDABLE SCs only (the small
|
|
286
|
+
// static set + any judgment SC that fired a definite NC). NOT a full conformance
|
|
287
|
+
// rate — most SCs are needs-rendering/judgment and stay manual (residual risk).
|
|
288
|
+
conformancePct: number;
|
|
289
|
+
// Set once `verify --apply <adjudication>` has folded an AI adjudication of the manual
|
|
290
|
+
// criteria back into the audit (src/adjudicate.ts). `stillManual` = criteria the agent
|
|
291
|
+
// left as an explicit residual (needs a rendered DOM → `scan`, or genuinely undecidable).
|
|
292
|
+
// Optional/additive — absent on a plain engine audit.
|
|
293
|
+
adjudicated?: { date: string; applied: number; stillManual: number };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---- optional dynamic tier (axe-core in a headless browser): Docker image, or a
|
|
297
|
+
// host/target Playwright resolved at runtime (`scan --local`). `engine` widens from
|
|
298
|
+
// the axe + 320px-reflow pair to the bespoke residual-criteria probes the local
|
|
299
|
+
// runtime adds (focus visibility, 200% zoom reflow, text spacing, content-on-hover)
|
|
300
|
+
// — the criteria axe alone cannot decide. (Target size 2.5.8 is left to axe's own rule.)
|
|
301
|
+
//
|
|
302
|
+
// The `input-overflow-*` and `live-region` engines are STATEFUL: they only ever
|
|
303
|
+
// populate when the local runtime runs with interactions ON (the default; `scan
|
|
304
|
+
// --no-interact` disables them). Each measures the page AFTER a real user action —
|
|
305
|
+
// long values typed into inputs, or a safe interaction (fill / toggle / click a
|
|
306
|
+
// type=button) — the class of non-conformity a pristine page never reveals. The
|
|
307
|
+
// three input-overflow engines share the "filled input clipped/unreadable" check but
|
|
308
|
+
// map to a different SC per stress (320px reflow 1.4.10, 200% zoom 1.4.4, text
|
|
309
|
+
// spacing 1.4.12); live-region maps to 4.1.3 Status Messages.
|
|
310
|
+
export type DynamicEngine =
|
|
311
|
+
| "axe"
|
|
312
|
+
| "reflow"
|
|
313
|
+
| "focus-visible"
|
|
314
|
+
| "reflow-zoom"
|
|
315
|
+
| "text-spacing"
|
|
316
|
+
| "hover"
|
|
317
|
+
| "input-overflow-reflow"
|
|
318
|
+
| "input-overflow-zoom"
|
|
319
|
+
| "input-overflow-spacing"
|
|
320
|
+
| "live-region";
|
|
321
|
+
|
|
322
|
+
export interface DynamicFinding {
|
|
323
|
+
criteriaId: string;
|
|
324
|
+
axeRule: string;
|
|
325
|
+
impact: string;
|
|
326
|
+
severity: Severity;
|
|
327
|
+
message: string;
|
|
328
|
+
selector: string;
|
|
329
|
+
snippet: string;
|
|
330
|
+
engine: DynamicEngine;
|
|
331
|
+
page?: string; // the scanned URL/page this finding came from (multi-page crawl)
|
|
332
|
+
// Best-practice-only dynamic finding (no `wcag*` tag on the axe violation): folds into
|
|
333
|
+
// the audit as an advisory recommendation, never a criterion NC. See Finding.advisory
|
|
334
|
+
// and src/axe-map.ts `axeAdvisory`. Optional/additive — absent ⇒ normative.
|
|
335
|
+
advisory?: boolean;
|
|
336
|
+
// Per-sample provenance (`scan --sample`): the originating sample page. Carried through
|
|
337
|
+
// `mergeDynamic` onto the merged Finding's `sample` for ticket rendering. Optional.
|
|
338
|
+
sample?: { id: string; name: string; auth?: boolean; notes?: string };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface DynamicResult {
|
|
342
|
+
tool: "ultra11y";
|
|
343
|
+
engine: string; // e.g. "axe-core@playwright (docker)"
|
|
344
|
+
target: string;
|
|
345
|
+
date: string;
|
|
346
|
+
findings: DynamicFinding[];
|
|
347
|
+
// Set by a `scan --sample` run: the normative page sample scanned (recorded onto the
|
|
348
|
+
// merged AuditResult's scope.sample). Optional/additive — absent for a plain scan.
|
|
349
|
+
sample?: SampleScope;
|
|
350
|
+
// Which needs-rendering SCs this run's engines/probes actually MEASURED (verdict
|
|
351
|
+
// coverage — independent of whether anything was found). Docker: 320px reflow only;
|
|
352
|
+
// local: + zoom / text spacing / focus / hover (+ live regions when interactions are
|
|
353
|
+
// on). Merged into AuditResult.scope.scan by mergeDynamic. Optional/additive.
|
|
354
|
+
testedScs?: string[];
|
|
355
|
+
}
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Small shared utilities. Runtime-only Node APIs (Date, fs) are fine here — the
|
|
2
|
+
// "no Date" rule applies to workflow scripts, not the CLI bundle.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { extname } from "node:path";
|
|
5
|
+
|
|
6
|
+
export function escapeRegExp(s: string): string {
|
|
7
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Today as YYYY-MM-DD (local-agnostic, ISO date). */
|
|
11
|
+
export function today(): string {
|
|
12
|
+
return new Date().toISOString().slice(0, 10);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readText(path: string): string {
|
|
16
|
+
return readFileSync(path, "utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ext(path: string): string {
|
|
20
|
+
return extname(path).toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function uniq<T>(items: T[]): T[] {
|
|
24
|
+
return [...new Set(items)];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Read all of stdin as text (for `audit -`). */
|
|
28
|
+
export async function readStdin(): Promise<string> {
|
|
29
|
+
const chunks: Buffer[] = [];
|
|
30
|
+
for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
|
|
31
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
32
|
+
}
|