ultra11y 5.15.1 → 5.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/README.md +1 -1
- package/package.json +3 -3
- package/scripts/ultra11y.mjs +32 -12
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/scripts/vendor/wcag-2.2-techniques.json +1 -1
- package/scripts/vendor/wcag-2.2-text.json +1 -1
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +32 -12
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/references/dynamic.md +12 -7
- package/skills/ultra11y/scripts/ultra11y.mjs +32 -12
- package/src/data/wcag-universe.json +1 -1
- package/src/scan-local.ts +50 -13
- package/src/types.ts +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review of the code under change before every commit, push and pull request, plus full WCAG 2.2 AA / RGAA audits and conformance deliverables on demand.",
|
|
13
|
-
"version": "5.
|
|
13
|
+
"version": "5.16.0",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
15
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
16
16
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.16.0",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.16.0",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
|
|
|
27
27
|
// Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
|
|
28
28
|
// does not carry it, so a user's own ultra11y.js is never clobbered.
|
|
29
29
|
const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
30
|
-
const ULTRA11Y_PLUGIN_VERSION = "5.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.16.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -442,7 +442,7 @@ node scripts/ultra11y.mjs scan http://localhost:3000 --runtime local --cwd . --m
|
|
|
442
442
|
node scripts/ultra11y.mjs report --in audits/audit-latest.json --out audits
|
|
443
443
|
```
|
|
444
444
|
|
|
445
|
-
Only the Docker runtime needs Docker
|
|
445
|
+
Only the Docker runtime needs Docker. `--runtime local` needs a Chromium binary (`npx playwright install chromium`) and nothing else: `@playwright/test` and `@axe-core/playwright` are dependencies of the package, and the audited project's own copies take precedence when it has them. The engine bundle itself stays dependency-free. The Docker runner + Dockerfile are embedded in the engine and mirrored under `docker/` (with a `docker-compose.yml`). See `skills/ultra11y/references/dynamic.md`.
|
|
446
446
|
|
|
447
447
|
Typical audit flow:
|
|
448
448
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.16.0",
|
|
4
4
|
"description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|
|
@@ -77,14 +77,14 @@
|
|
|
77
77
|
"url": "https://github.com/maxgfr/ultra11y"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
+
"@axe-core/playwright": "^4.13.0",
|
|
80
81
|
"@babel/parser": "^8.0.0",
|
|
82
|
+
"@playwright/test": "^1.62.1",
|
|
81
83
|
"domhandler": "^6.0.1",
|
|
82
84
|
"htmlparser2": "^12.0.0"
|
|
83
85
|
},
|
|
84
86
|
"devDependencies": {
|
|
85
|
-
"@axe-core/playwright": "^4.13.0",
|
|
86
87
|
"@biomejs/biome": "^2.5.1",
|
|
87
|
-
"@playwright/test": "^1.62.1",
|
|
88
88
|
"@semantic-release/changelog": "^6.0.3",
|
|
89
89
|
"@semantic-release/exec": "^7.1.0",
|
|
90
90
|
"@semantic-release/git": "^10.0.1",
|
package/scripts/ultra11y.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { join as join51, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.
|
|
9
|
+
var VERSION = "5.16.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -2290,7 +2290,7 @@ var wcag_universe_default = {
|
|
|
2290
2290
|
wcagVersion: "2.2",
|
|
2291
2291
|
source: "https://www.w3.org/TR/WCAG22/",
|
|
2292
2292
|
criteriaSource: "https://github.com/w3c/wcag",
|
|
2293
|
-
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-
|
|
2293
|
+
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-20 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
|
|
2294
2294
|
criteria: [
|
|
2295
2295
|
{
|
|
2296
2296
|
id: "1.1.1",
|
|
@@ -59894,17 +59894,38 @@ function localTestedScs(interact) {
|
|
|
59894
59894
|
}
|
|
59895
59895
|
var PW_SPEC = "@playwright/test";
|
|
59896
59896
|
var AXE_SPEC = "@axe-core/playwright";
|
|
59897
|
-
function
|
|
59898
|
-
|
|
59899
|
-
|
|
59897
|
+
function resolveAnchors(cwd) {
|
|
59898
|
+
return [resolve12(cwd, "package.json"), import.meta.url];
|
|
59899
|
+
}
|
|
59900
|
+
function requireRuntime(cwd, spec) {
|
|
59901
|
+
let last;
|
|
59902
|
+
for (const from of resolveAnchors(cwd)) {
|
|
59903
|
+
try {
|
|
59904
|
+
return createRequire(from)(spec);
|
|
59905
|
+
} catch (e) {
|
|
59906
|
+
last = e;
|
|
59907
|
+
}
|
|
59908
|
+
}
|
|
59909
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
59910
|
+
}
|
|
59911
|
+
function resolvesRuntime(cwd, spec) {
|
|
59912
|
+
return resolveAnchors(cwd).some((from) => {
|
|
59900
59913
|
try {
|
|
59901
|
-
|
|
59914
|
+
createRequire(from).resolve(spec);
|
|
59915
|
+
return true;
|
|
59902
59916
|
} catch {
|
|
59903
|
-
return
|
|
59917
|
+
return false;
|
|
59918
|
+
}
|
|
59919
|
+
});
|
|
59920
|
+
}
|
|
59921
|
+
function localTierStatus(cwd) {
|
|
59922
|
+
for (const spec of [PW_SPEC, AXE_SPEC]) {
|
|
59923
|
+
if (!resolvesRuntime(cwd, spec)) {
|
|
59924
|
+
return { ok: false, reason: `${spec} resolves neither from ${cwd} nor from ultra11y itself` };
|
|
59904
59925
|
}
|
|
59905
59926
|
}
|
|
59906
59927
|
try {
|
|
59907
|
-
const pw =
|
|
59928
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59908
59929
|
const bin = pw.chromium?.executablePath?.();
|
|
59909
59930
|
if (typeof bin === "string" && bin.length > 0 && !existsSync24(bin)) {
|
|
59910
59931
|
return { ok: false, reason: `no browser binary at ${bin} \u2014 run \`npx playwright install chromium\`` };
|
|
@@ -59921,15 +59942,14 @@ function resolveLocalDeps(cwd) {
|
|
|
59921
59942
|
let chromium;
|
|
59922
59943
|
let AxeBuilder;
|
|
59923
59944
|
try {
|
|
59924
|
-
const
|
|
59925
|
-
const
|
|
59926
|
-
const axeMod = req(AXE_SPEC);
|
|
59945
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59946
|
+
const axeMod = requireRuntime(cwd, AXE_SPEC);
|
|
59927
59947
|
chromium = pw.chromium;
|
|
59928
59948
|
AxeBuilder = axeMod.default ?? axeMod;
|
|
59929
59949
|
} catch (e) {
|
|
59930
59950
|
const msg = e instanceof Error ? e.message : String(e);
|
|
59931
59951
|
throw new Error(
|
|
59932
|
-
`Playwright
|
|
59952
|
+
`Playwright resolves neither from "${cwd}" nor from ultra11y itself. Both are dependencies of this package, so the usual cause is the standalone engine bundle running with no node_modules beside it: pass --cwd <dir> at a project that has @playwright/test + @axe-core/playwright (e.g. --cwd packages/app), or use --runtime docker. (${msg})`
|
|
59933
59953
|
);
|
|
59934
59954
|
}
|
|
59935
59955
|
if (!chromium || typeof AxeBuilder !== "function") {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"wcagVersion": "2.2",
|
|
3
3
|
"source": "https://www.w3.org/TR/WCAG22/",
|
|
4
4
|
"criteriaSource": "https://github.com/w3c/wcag",
|
|
5
|
-
"provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-
|
|
5
|
+
"provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-20 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
|
|
6
6
|
"principles": [
|
|
7
7
|
{
|
|
8
8
|
"number": 1,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"source": "https://github.com/w3c/wcag",
|
|
3
3
|
"understanding": "https://www.w3.org/WAI/WCAG22/Understanding/",
|
|
4
4
|
"license": "W3C Document License (code samples: W3C Software License)",
|
|
5
|
-
"fetchedAt": "2026-08-
|
|
5
|
+
"fetchedAt": "2026-08-20",
|
|
6
6
|
"criteria": {
|
|
7
7
|
"1.1.1": {
|
|
8
8
|
"slug": "non-text-content",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "https://github.com/w3c/wcag",
|
|
3
3
|
"license": "W3C Document License",
|
|
4
|
-
"fetchedAt": "2026-08-
|
|
4
|
+
"fetchedAt": "2026-08-20",
|
|
5
5
|
"criteria": {
|
|
6
6
|
"1.1.1": "All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below.\nControls, Input:\nIf non-text content is a control or accepts user input, then it has a name that describes its purpose. (Refer to Success Criterion 4.1.2 Name, Role, Value for additional requirements for controls and content that accepts user input.)\nTime-Based Media:\nIf non-text content is time-based media, then text alternatives at least provide descriptive identification of the non-text content. (Refer to Guideline 1.2 for additional requirements for media.)\nTest:\nIf non-text content is a test or exercise that would be invalid if presented in text, then text alternatives at least provide descriptive identification of the non-text content.\nSensory:\nIf non-text content is primarily intended to create a specific sensory experience, then text alternatives at least provide descriptive identification of the non-text content.\nCAPTCHA:\nIf the purpose of non-text content is to confirm that content is being accessed by a person rather than a computer, then text alternatives that identify and describe the purpose of the non-text content are provided, and alternative forms of CAPTCHA using output modes for different types of sensory perception are provided to accommodate different disabilities.\nDecoration, Formatting, Invisible:\nIf non-text content is pure decoration, is used only for visual formatting, or is not presented to users, then it is implemented in a way that it can be ignored by assistive technology.",
|
|
7
7
|
"1.2.1": "For prerecorded audio-only and prerecorded video-only media, the following are true, except when the audio or video is a media alternative for text and is clearly labeled as such:\nPrerecorded Audio-only:\nAn alternative for time-based media is provided that presents equivalent information for prerecorded audio-only content.\nPrerecorded Video-only:\nEither an alternative for time-based media or an audio track is provided that presents equivalent information for prerecorded video-only content.",
|
|
@@ -5,7 +5,7 @@ when_to_use: "Invoke WITHOUT being asked whenever changed front-end code should
|
|
|
5
5
|
allowed-tools: Bash(node ${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs *), Read, Grep, Glob
|
|
6
6
|
license: MIT
|
|
7
7
|
metadata:
|
|
8
|
-
version: 5.
|
|
8
|
+
version: 5.16.0
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# review-a11y — accessibility review of changed code
|
|
@@ -6,7 +6,7 @@ import { join as join51, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.
|
|
9
|
+
var VERSION = "5.16.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -2290,7 +2290,7 @@ var wcag_universe_default = {
|
|
|
2290
2290
|
wcagVersion: "2.2",
|
|
2291
2291
|
source: "https://www.w3.org/TR/WCAG22/",
|
|
2292
2292
|
criteriaSource: "https://github.com/w3c/wcag",
|
|
2293
|
-
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-
|
|
2293
|
+
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-20 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
|
|
2294
2294
|
criteria: [
|
|
2295
2295
|
{
|
|
2296
2296
|
id: "1.1.1",
|
|
@@ -59894,17 +59894,38 @@ function localTestedScs(interact) {
|
|
|
59894
59894
|
}
|
|
59895
59895
|
var PW_SPEC = "@playwright/test";
|
|
59896
59896
|
var AXE_SPEC = "@axe-core/playwright";
|
|
59897
|
-
function
|
|
59898
|
-
|
|
59899
|
-
|
|
59897
|
+
function resolveAnchors(cwd) {
|
|
59898
|
+
return [resolve12(cwd, "package.json"), import.meta.url];
|
|
59899
|
+
}
|
|
59900
|
+
function requireRuntime(cwd, spec) {
|
|
59901
|
+
let last;
|
|
59902
|
+
for (const from of resolveAnchors(cwd)) {
|
|
59903
|
+
try {
|
|
59904
|
+
return createRequire(from)(spec);
|
|
59905
|
+
} catch (e) {
|
|
59906
|
+
last = e;
|
|
59907
|
+
}
|
|
59908
|
+
}
|
|
59909
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
59910
|
+
}
|
|
59911
|
+
function resolvesRuntime(cwd, spec) {
|
|
59912
|
+
return resolveAnchors(cwd).some((from) => {
|
|
59900
59913
|
try {
|
|
59901
|
-
|
|
59914
|
+
createRequire(from).resolve(spec);
|
|
59915
|
+
return true;
|
|
59902
59916
|
} catch {
|
|
59903
|
-
return
|
|
59917
|
+
return false;
|
|
59918
|
+
}
|
|
59919
|
+
});
|
|
59920
|
+
}
|
|
59921
|
+
function localTierStatus(cwd) {
|
|
59922
|
+
for (const spec of [PW_SPEC, AXE_SPEC]) {
|
|
59923
|
+
if (!resolvesRuntime(cwd, spec)) {
|
|
59924
|
+
return { ok: false, reason: `${spec} resolves neither from ${cwd} nor from ultra11y itself` };
|
|
59904
59925
|
}
|
|
59905
59926
|
}
|
|
59906
59927
|
try {
|
|
59907
|
-
const pw =
|
|
59928
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59908
59929
|
const bin = pw.chromium?.executablePath?.();
|
|
59909
59930
|
if (typeof bin === "string" && bin.length > 0 && !existsSync24(bin)) {
|
|
59910
59931
|
return { ok: false, reason: `no browser binary at ${bin} \u2014 run \`npx playwright install chromium\`` };
|
|
@@ -59921,15 +59942,14 @@ function resolveLocalDeps(cwd) {
|
|
|
59921
59942
|
let chromium;
|
|
59922
59943
|
let AxeBuilder;
|
|
59923
59944
|
try {
|
|
59924
|
-
const
|
|
59925
|
-
const
|
|
59926
|
-
const axeMod = req(AXE_SPEC);
|
|
59945
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59946
|
+
const axeMod = requireRuntime(cwd, AXE_SPEC);
|
|
59927
59947
|
chromium = pw.chromium;
|
|
59928
59948
|
AxeBuilder = axeMod.default ?? axeMod;
|
|
59929
59949
|
} catch (e) {
|
|
59930
59950
|
const msg = e instanceof Error ? e.message : String(e);
|
|
59931
59951
|
throw new Error(
|
|
59932
|
-
`Playwright
|
|
59952
|
+
`Playwright resolves neither from "${cwd}" nor from ultra11y itself. Both are dependencies of this package, so the usual cause is the standalone engine bundle running with no node_modules beside it: pass --cwd <dir> at a project that has @playwright/test + @axe-core/playwright (e.g. --cwd packages/app), or use --runtime docker. (${msg})`
|
|
59933
59953
|
);
|
|
59934
59954
|
}
|
|
59935
59955
|
if (!chromium || typeof AxeBuilder !== "function") {
|
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: "Use to AUDIT existing HTML/CSS/JSX against WCAG 2.2 AA accessibili
|
|
|
4
4
|
when_to_use: "Invoke when the user asks for an accessibility AUDIT or a formal deliverable: audit a repo, site or page against WCAG 2.2 AA or a country standard (RGAA, Section 508, EN 301 549); produce a dated conformance report, a criterion grid, a PRD backlog, or tickets (GitHub/GitLab/Jira); author accessible markup from scratch; wire the repo gate (init --hook/--ci) or a rendered-DOM capture pipeline. For a review of the code UNDER CHANGE (diff, branch, PR), use the `review-a11y` skill instead."
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: 5.
|
|
7
|
+
version: 5.16.0
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -5,18 +5,23 @@ contrast (1.4.3), focus visible (2.4.7), reflow/zoom (1.4.4/1.4.10), text spacin
|
|
|
5
5
|
content on hover (1.4.13), target size (2.5.8). The dynamic tier decides them by running
|
|
6
6
|
**axe-core in a real headless browser** (Playwright). Two runtimes, same finding shape:
|
|
7
7
|
|
|
8
|
-
- **`--runtime local`** (recommended): resolves
|
|
9
|
-
**at runtime
|
|
10
|
-
|
|
8
|
+
- **`--runtime local`** (recommended): resolves Playwright + `@axe-core/playwright`
|
|
9
|
+
**at runtime**, from `--cwd` first and from ultra11y's own install second (no Docker, no
|
|
10
|
+
global install). Both are dependencies of this package, so a project that installed ultra11y
|
|
11
|
+
alone still gets the tier; a project that pins its own Playwright keeps it, because `--cwd`
|
|
12
|
+
is tried first. Adds the residual-criteria **probes** (below).
|
|
11
13
|
- **`--runtime docker`**: runs axe-core in a Docker image auto-built on first use (runner +
|
|
12
14
|
Dockerfile embedded in the bundle). No host deps beyond Docker. Axe + 320px reflow only.
|
|
13
|
-
- **`--runtime auto`** (default): local if Playwright resolves
|
|
14
|
-
an actionable error.
|
|
15
|
+
- **`--runtime auto`** (default): local if Playwright resolves **and** a browser binary is on
|
|
16
|
+
disk, else Docker, else an actionable error. Since the packages ship with ultra11y, what
|
|
17
|
+
decides `auto` in practice is the browser, not the modules.
|
|
15
18
|
|
|
16
19
|
## Prerequisites
|
|
17
20
|
|
|
18
|
-
- **local**: a
|
|
19
|
-
|
|
21
|
+
- **local**: a Chromium browser — `npx playwright install chromium`. The two npm packages
|
|
22
|
+
(`@playwright/test`, `@axe-core/playwright`) come with ultra11y and need no separate install;
|
|
23
|
+
installing them in the audited project is still supported and takes precedence, which is what
|
|
24
|
+
a repository with its own pinned Playwright wants.
|
|
20
25
|
- **docker**: Docker running.
|
|
21
26
|
|
|
22
27
|
The rest of the skill (static audit) needs neither.
|
|
@@ -6,7 +6,7 @@ import { join as join51, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.
|
|
9
|
+
var VERSION = "5.16.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -2290,7 +2290,7 @@ var wcag_universe_default = {
|
|
|
2290
2290
|
wcagVersion: "2.2",
|
|
2291
2291
|
source: "https://www.w3.org/TR/WCAG22/",
|
|
2292
2292
|
criteriaSource: "https://github.com/w3c/wcag",
|
|
2293
|
-
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-
|
|
2293
|
+
provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-20 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
|
|
2294
2294
|
criteria: [
|
|
2295
2295
|
{
|
|
2296
2296
|
id: "1.1.1",
|
|
@@ -59894,17 +59894,38 @@ function localTestedScs(interact) {
|
|
|
59894
59894
|
}
|
|
59895
59895
|
var PW_SPEC = "@playwright/test";
|
|
59896
59896
|
var AXE_SPEC = "@axe-core/playwright";
|
|
59897
|
-
function
|
|
59898
|
-
|
|
59899
|
-
|
|
59897
|
+
function resolveAnchors(cwd) {
|
|
59898
|
+
return [resolve12(cwd, "package.json"), import.meta.url];
|
|
59899
|
+
}
|
|
59900
|
+
function requireRuntime(cwd, spec) {
|
|
59901
|
+
let last;
|
|
59902
|
+
for (const from of resolveAnchors(cwd)) {
|
|
59903
|
+
try {
|
|
59904
|
+
return createRequire(from)(spec);
|
|
59905
|
+
} catch (e) {
|
|
59906
|
+
last = e;
|
|
59907
|
+
}
|
|
59908
|
+
}
|
|
59909
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
59910
|
+
}
|
|
59911
|
+
function resolvesRuntime(cwd, spec) {
|
|
59912
|
+
return resolveAnchors(cwd).some((from) => {
|
|
59900
59913
|
try {
|
|
59901
|
-
|
|
59914
|
+
createRequire(from).resolve(spec);
|
|
59915
|
+
return true;
|
|
59902
59916
|
} catch {
|
|
59903
|
-
return
|
|
59917
|
+
return false;
|
|
59918
|
+
}
|
|
59919
|
+
});
|
|
59920
|
+
}
|
|
59921
|
+
function localTierStatus(cwd) {
|
|
59922
|
+
for (const spec of [PW_SPEC, AXE_SPEC]) {
|
|
59923
|
+
if (!resolvesRuntime(cwd, spec)) {
|
|
59924
|
+
return { ok: false, reason: `${spec} resolves neither from ${cwd} nor from ultra11y itself` };
|
|
59904
59925
|
}
|
|
59905
59926
|
}
|
|
59906
59927
|
try {
|
|
59907
|
-
const pw =
|
|
59928
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59908
59929
|
const bin = pw.chromium?.executablePath?.();
|
|
59909
59930
|
if (typeof bin === "string" && bin.length > 0 && !existsSync24(bin)) {
|
|
59910
59931
|
return { ok: false, reason: `no browser binary at ${bin} \u2014 run \`npx playwright install chromium\`` };
|
|
@@ -59921,15 +59942,14 @@ function resolveLocalDeps(cwd) {
|
|
|
59921
59942
|
let chromium;
|
|
59922
59943
|
let AxeBuilder;
|
|
59923
59944
|
try {
|
|
59924
|
-
const
|
|
59925
|
-
const
|
|
59926
|
-
const axeMod = req(AXE_SPEC);
|
|
59945
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
59946
|
+
const axeMod = requireRuntime(cwd, AXE_SPEC);
|
|
59927
59947
|
chromium = pw.chromium;
|
|
59928
59948
|
AxeBuilder = axeMod.default ?? axeMod;
|
|
59929
59949
|
} catch (e) {
|
|
59930
59950
|
const msg = e instanceof Error ? e.message : String(e);
|
|
59931
59951
|
throw new Error(
|
|
59932
|
-
`Playwright
|
|
59952
|
+
`Playwright resolves neither from "${cwd}" nor from ultra11y itself. Both are dependencies of this package, so the usual cause is the standalone engine bundle running with no node_modules beside it: pass --cwd <dir> at a project that has @playwright/test + @axe-core/playwright (e.g. --cwd packages/app), or use --runtime docker. (${msg})`
|
|
59933
59953
|
);
|
|
59934
59954
|
}
|
|
59935
59955
|
if (!chromium || typeof AxeBuilder !== "function") {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"wcagVersion": "2.2",
|
|
3
3
|
"source": "https://www.w3.org/TR/WCAG22/",
|
|
4
4
|
"criteriaSource": "https://github.com/w3c/wcag",
|
|
5
|
-
"provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-
|
|
5
|
+
"provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-20 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
|
|
6
6
|
"criteria": [
|
|
7
7
|
{
|
|
8
8
|
"id": "1.1.1",
|
package/src/scan-local.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// `scan --runtime local` — the dynamic tier WITHOUT Docker. Resolves
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// `scan --runtime local` — the dynamic tier WITHOUT Docker. Resolves Playwright +
|
|
2
|
+
// @axe-core/playwright AT RUNTIME (createRequire, a node: builtin) from `--cwd` first and
|
|
3
|
+
// from this package second, so the zero-dep static engine bundle never gains a static
|
|
4
|
+
// browser import while a project that installed ultra11y alone still gets the tier.
|
|
4
5
|
// It runs the SAME axe-core pass as the Docker RUNNER, then the residual-criteria
|
|
5
6
|
// probes axe cannot decide (focus visibility, 200% zoom, text spacing, content on
|
|
6
7
|
// hover, target size). The Docker RUNNER / docker/* files are untouched (docker-sync).
|
|
@@ -59,6 +60,46 @@ interface LocalDeps {
|
|
|
59
60
|
const PW_SPEC = "@playwright/test";
|
|
60
61
|
const AXE_SPEC = "@axe-core/playwright";
|
|
61
62
|
|
|
63
|
+
/** Where a runtime dependency may live, in the order that must be tried.
|
|
64
|
+
*
|
|
65
|
+
* THE TARGET PROJECT FIRST, then this package — the same order `src/integrations/playwright.ts`
|
|
66
|
+
* uses, and for the same two reasons. A repository that pins its own Playwright must keep it:
|
|
67
|
+
* two copies in one process hand out `Page` objects the other one's fixtures do not recognise.
|
|
68
|
+
* And a repository that has none must still get the tier, now that `@playwright/test` and
|
|
69
|
+
* `@axe-core/playwright` are dependencies of ultra11y instead of something every caller had to
|
|
70
|
+
* duplicate into its own manifest. Anchoring on `cwd` ALONE was the older contract, and it made
|
|
71
|
+
* `--runtime local` unavailable to exactly the projects that had not read the prerequisites. */
|
|
72
|
+
function resolveAnchors(cwd: string): string[] {
|
|
73
|
+
return [resolve(cwd, "package.json"), import.meta.url];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Load `spec` from the target project, else from this package. Rethrows the LAST failure, so
|
|
77
|
+
* the message names the spec the caller asked for rather than the first anchor that missed. */
|
|
78
|
+
function requireRuntime(cwd: string, spec: string): Any {
|
|
79
|
+
let last: unknown;
|
|
80
|
+
for (const from of resolveAnchors(cwd)) {
|
|
81
|
+
try {
|
|
82
|
+
return createRequire(from)(spec);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
last = e;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Can `spec` be resolved from either anchor? Resolution only — never loads the module, so
|
|
91
|
+
* probing the tier stays as cheap as it was when it only looked at `cwd`. */
|
|
92
|
+
function resolvesRuntime(cwd: string, spec: string): boolean {
|
|
93
|
+
return resolveAnchors(cwd).some((from) => {
|
|
94
|
+
try {
|
|
95
|
+
createRequire(from).resolve(spec);
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
62
103
|
/** Why the LOCAL tier is (un)usable from `cwd` — the one line a reader of a
|
|
63
104
|
* `runtime: auto` log gets before the Docker fallback takes over. A silent degrade is
|
|
64
105
|
* indistinguishable from a working fallback, so the reason must NAME what to install. */
|
|
@@ -68,16 +109,13 @@ export type LocalTierStatus = { ok: true } | { ok: false; reason: string };
|
|
|
68
109
|
* boolean. Drives the `runtime: auto` decision AND the message printed when it degrades
|
|
69
110
|
* to Docker. */
|
|
70
111
|
export function localTierStatus(cwd: string): LocalTierStatus {
|
|
71
|
-
const req = createRequire(resolve(cwd, "package.json"));
|
|
72
112
|
for (const spec of [PW_SPEC, AXE_SPEC]) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
} catch {
|
|
76
|
-
return { ok: false, reason: `${spec} does not resolve from ${cwd}` };
|
|
113
|
+
if (!resolvesRuntime(cwd, spec)) {
|
|
114
|
+
return { ok: false, reason: `${spec} resolves neither from ${cwd} nor from ultra11y itself` };
|
|
77
115
|
}
|
|
78
116
|
}
|
|
79
117
|
try {
|
|
80
|
-
const pw =
|
|
118
|
+
const pw = requireRuntime(cwd, PW_SPEC) as { chromium?: { executablePath?: () => string } };
|
|
81
119
|
const bin = pw.chromium?.executablePath?.();
|
|
82
120
|
// No path at all ⇒ a Playwright that cannot tell us; treat as available and let the
|
|
83
121
|
// launch report its own failure, rather than refusing a tier that might work.
|
|
@@ -112,15 +150,14 @@ export function resolveLocalDeps(cwd: string): LocalDeps {
|
|
|
112
150
|
let chromium: Any;
|
|
113
151
|
let AxeBuilder: Any;
|
|
114
152
|
try {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const axeMod = req(AXE_SPEC);
|
|
153
|
+
const pw = requireRuntime(cwd, PW_SPEC);
|
|
154
|
+
const axeMod = requireRuntime(cwd, AXE_SPEC);
|
|
118
155
|
chromium = pw.chromium;
|
|
119
156
|
AxeBuilder = axeMod.default ?? axeMod;
|
|
120
157
|
} catch (e) {
|
|
121
158
|
const msg = e instanceof Error ? e.message : String(e);
|
|
122
159
|
throw new Error(
|
|
123
|
-
`Playwright
|
|
160
|
+
`Playwright resolves neither from "${cwd}" nor from ultra11y itself. Both are dependencies of this package, so the usual cause is the standalone engine bundle running with no node_modules beside it: pass --cwd <dir> at a project that has @playwright/test + @axe-core/playwright (e.g. --cwd packages/app), or use --runtime docker. (${msg})`,
|
|
124
161
|
);
|
|
125
162
|
}
|
|
126
163
|
if (!chromium || typeof AxeBuilder !== "function") {
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Single source of truth for shared types + the embedded version. sync-version.mjs
|
|
2
2
|
// rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
|
|
3
|
-
export const VERSION = "5.
|
|
3
|
+
export const VERSION = "5.16.0";
|
|
4
4
|
export const SCHEMA_VERSION = 2;
|
|
5
5
|
|
|
6
6
|
// Lang = the UI FRAME's language — the `L` localization tables each module (report,
|