ultra11y 5.43.5 → 5.45.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 +1 -1
- package/scripts/ultra11y.mjs +36 -9
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +36 -9
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/references/dynamic.md +2 -1
- package/skills/ultra11y/references/orchestrators.md +10 -3
- package/skills/ultra11y/scripts/ultra11y.mjs +36 -9
- package/skills/ultra11y/templates/gitlab-ci.yml +1 -1
- package/src/cli.ts +33 -8
- package/src/scan.ts +26 -1
- package/src/types.ts +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page WCAG 2.2 AA / RGAA reports in Claude Code.",
|
|
13
|
-
"version": "5.
|
|
13
|
+
"version": "5.45.0",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
15
|
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
16
16
|
"repository": "https://github.com/SocialGouv/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.45.0",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
7
7
|
"repository": "https://github.com/SocialGouv/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.45.0",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
7
7
|
"repository": "https://github.com/SocialGouv/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.45.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ ultra11y verify --report <md> [--conformities <ledger|adjudication.json> | --n
|
|
|
160
160
|
ultra11y fix <globs… | -> [--write] [--iterate] [--changed | --since <ref> | --staged] [--safe] [--only <ids>] [--jsx] [--json]
|
|
161
161
|
ultra11y init [--hook] [--ci] [--baseline] [--fail-on blocking|major|minor]
|
|
162
162
|
ultra11y pack check <pack.json> [--guidance <g.json>] | scaffold # gate an (AI-)authored standards pack
|
|
163
|
-
ultra11y scan <url|file…> [--runtime auto|local|docker] [--cwd <dir>] [--storage-state <file>] [--merge <audit.json>] [--out <dir>] [--json]
|
|
163
|
+
ultra11y scan <url|file…> [--runtime auto|local|docker] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--merge <audit.json>] [--out <dir>] [--json]
|
|
164
164
|
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime …] [--merge <audit.json>] [--json]
|
|
165
165
|
ultra11y mcp [--transport stdio|http] [--cwd <dir>] [--allow-write] [--port <n>] [--bind <addr>] [--allow-remote]
|
|
166
166
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.45.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",
|
package/scripts/ultra11y.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { join as join52, relative as relative6, resolve as resolve17, 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.45.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -66879,6 +66879,13 @@ function resolveHostAnchor(file, snippet2) {
|
|
|
66879
66879
|
}
|
|
66880
66880
|
return null;
|
|
66881
66881
|
}
|
|
66882
|
+
function scanPageSummary(dynamic, sampleSize) {
|
|
66883
|
+
return {
|
|
66884
|
+
...sampleSize === void 0 ? {} : { sample: sampleSize },
|
|
66885
|
+
recorded: dynamic.snapshots ?? [],
|
|
66886
|
+
refused: (dynamic.redirected ?? []).map((r) => ({ id: r.id, reason: r.reason }))
|
|
66887
|
+
};
|
|
66888
|
+
}
|
|
66882
66889
|
function mergeDynamic(audit2, dynamic, lang = "en") {
|
|
66883
66890
|
const merged = JSON.parse(JSON.stringify(audit2));
|
|
66884
66891
|
const seenFindings = new Set(merged.findings.map(portableFindingId));
|
|
@@ -74160,9 +74167,9 @@ Usage:
|
|
|
74160
74167
|
ultra11y init [--hook] [--ci] [--baseline] [--fail-on blocking|major|minor]
|
|
74161
74168
|
ultra11y judge --in <audit.json> [--standard <pack>] [--runner api|claude|codex] [--grain batch|criterion] [--max <n>] [--model <id>] [--effort <level>] [--max-budget-usd <n>] [--timeout <s>] [--out <dir>] [--apply] (adjudicate manual criteria \u2014 api needs ANTHROPIC_API_KEY; claude/codex reuse their CLI login; cli aliases claude)
|
|
74162
74169
|
ultra11y pack check <pack.json> [--guidance <g.json>] [--json] | pack scaffold
|
|
74163
|
-
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74164
|
-
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74165
|
-
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--merge <audit.json>] [--json]
|
|
74170
|
+
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74171
|
+
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74172
|
+
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--merge <audit.json>] [--json]
|
|
74166
74173
|
ultra11y scan --clean (remove the dynamic-tier Docker image + temp contexts)
|
|
74167
74174
|
ultra11y sample check [--standard <pack>] [--json] (lint the .ultra11yrc.json page sample vs the standard's required page kinds)
|
|
74168
74175
|
ultra11y snapshot write [--root <dir>] [--fail-on blocking|major|minor] [--json] (payload on stdin \u2192 .ultra11y/pages/<id>/ + audit it)
|
|
@@ -74406,7 +74413,8 @@ Options:
|
|
|
74406
74413
|
--no-captures audit: do NOT auto-detect/ingest .ultra11y/captures nor .ultra11y/pages
|
|
74407
74414
|
--e2e render: write the Playwright/Cypress fixtures into .ultra11y/e2e/
|
|
74408
74415
|
--runner <name> render --e2e: force playwright|cypress instead of auto-detecting
|
|
74409
|
-
--root <dir> snapshot: project root holding .ultra11y/pages (default: .
|
|
74416
|
+
--root <dir> snapshot, scan: project root holding .ultra11y/pages (default: . \u2014
|
|
74417
|
+
scan writes the snapshots it collects under it)
|
|
74410
74418
|
--port <n> dev: port for the side-car (default: 4111); mcp: HTTP transport port
|
|
74411
74419
|
--transport <k> mcp: stdio (default) or http
|
|
74412
74420
|
--bind <addr> mcp --transport http: address to bind (default 127.0.0.1)
|
|
@@ -74492,7 +74500,9 @@ Options:
|
|
|
74492
74500
|
(exit 2 with the producing command if its worklist is missing)
|
|
74493
74501
|
--eco orchestrate: emit only RUNBOOK.md + agents/*.md \u2014 the explicit
|
|
74494
74502
|
sequential low-token path (also what a no-subagent harness follows)
|
|
74495
|
-
--merge <file> scan: fold dynamic findings into this AuditResult JSON
|
|
74503
|
+
--merge <file> scan: fold dynamic findings into this AuditResult JSON. With --json the
|
|
74504
|
+
printed document opens with a 'pages' summary of the run \u2014
|
|
74505
|
+
{sample, recorded, refused:[{id, reason}]} \u2014 beside the document
|
|
74496
74506
|
--sitemap <url> scan: scan every URL listed in a sitemap.xml
|
|
74497
74507
|
--crawl <url> scan: BFS same-origin links from a start URL (served HTML)
|
|
74498
74508
|
--depth <n> scan: crawl link-hop depth from the start URL (default: no limit; 0 = no limit)
|
|
@@ -77031,6 +77041,10 @@ Iterated over ${rounds} round(s) to a fixpoint \u2014 ${totalWritten} file(s) wr
|
|
|
77031
77041
|
}
|
|
77032
77042
|
return 0;
|
|
77033
77043
|
}
|
|
77044
|
+
function scanSnapshotRoot(flags2) {
|
|
77045
|
+
if (flags2["no-snapshot"] === true) return void 0;
|
|
77046
|
+
return typeof flags2.root === "string" && flags2.root ? resolve17(flags2.root) : process.cwd();
|
|
77047
|
+
}
|
|
77034
77048
|
async function cmdScan(p) {
|
|
77035
77049
|
const standard = stdOf(p, "scan");
|
|
77036
77050
|
if (standard === null) return 2;
|
|
@@ -77083,7 +77097,7 @@ async function cmdScan(p) {
|
|
|
77083
77097
|
const interactClicks = p.flags["interact-clicks"] === true;
|
|
77084
77098
|
const sitemap = typeof p.flags.sitemap === "string" ? p.flags.sitemap : void 0;
|
|
77085
77099
|
const crawl = typeof p.flags.crawl === "string" ? p.flags.crawl : void 0;
|
|
77086
|
-
const snapshotRoot = p.flags
|
|
77100
|
+
const snapshotRoot = scanSnapshotRoot(p.flags);
|
|
77087
77101
|
const useSample = p.flags.sample === true;
|
|
77088
77102
|
let sampleConfig;
|
|
77089
77103
|
if (useSample) {
|
|
@@ -77211,7 +77225,19 @@ async function cmdScan(p) {
|
|
|
77211
77225
|
mkdirSync18(out2, { recursive: true });
|
|
77212
77226
|
const mergedDocument = auditDocumentFor(merged, standard, lang);
|
|
77213
77227
|
writeFileSync21(join52(out2, "audit-latest.json"), JSON.stringify(mergedDocument, null, 2) + "\n");
|
|
77214
|
-
if (p.flags.json)
|
|
77228
|
+
if (p.flags.json)
|
|
77229
|
+
console.log(
|
|
77230
|
+
JSON.stringify(
|
|
77231
|
+
// `pages` opens the printed document: what this scan recorded (the
|
|
77232
|
+
// .ultra11y/pages ids) and refused, which the stderr warnings alone don't make
|
|
77233
|
+
// countable. An extra top-level key the `--in` readers never look at, so the
|
|
77234
|
+
// document beside it stays pipeable into report/verify — and the file above
|
|
77235
|
+
// keeps the plain audit schema.
|
|
77236
|
+
{ pages: scanPageSummary(dynamic, sampleConfig?.pages.length), ...mergedDocument },
|
|
77237
|
+
null,
|
|
77238
|
+
2
|
|
77239
|
+
)
|
|
77240
|
+
);
|
|
77215
77241
|
else {
|
|
77216
77242
|
console.log(
|
|
77217
77243
|
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
|
|
@@ -77768,6 +77794,7 @@ export {
|
|
|
77768
77794
|
COMMANDS,
|
|
77769
77795
|
REMOVED_FLAGS,
|
|
77770
77796
|
main,
|
|
77771
|
-
parseArgs
|
|
77797
|
+
parseArgs,
|
|
77798
|
+
scanSnapshotRoot
|
|
77772
77799
|
};
|
|
77773
77800
|
// "Copyright" and "@license" are already caught by DIRECTIVE_RE.
|
|
@@ -3,7 +3,7 @@ name: review-a11y
|
|
|
3
3
|
description: "Use to REVIEW changed frontend code for accessibility: staged files, a working diff, branch, or PR; prompts such as 'review a11y', 'is this accessible?', or 'anything to fix before merge?'; and hook or CI findings on a change. Run the bundled install-free engine with cross-file JSX/TSX analysis, then adjudicate candidates and return a severity-ranked WCAG 2.2 AA review with file:line fixes, residual rendering risks, and a verdict. Use `ultra11y` instead for whole-repo audits, dated conformance reports, country standards, PRD backlogs, or authoring accessible markup."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: 5.
|
|
6
|
+
version: 5.45.0
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# review-a11y — accessibility review of changed code
|
|
@@ -6,7 +6,7 @@ import { join as join52, relative as relative6, resolve as resolve17, 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.45.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -66879,6 +66879,13 @@ function resolveHostAnchor(file, snippet2) {
|
|
|
66879
66879
|
}
|
|
66880
66880
|
return null;
|
|
66881
66881
|
}
|
|
66882
|
+
function scanPageSummary(dynamic, sampleSize) {
|
|
66883
|
+
return {
|
|
66884
|
+
...sampleSize === void 0 ? {} : { sample: sampleSize },
|
|
66885
|
+
recorded: dynamic.snapshots ?? [],
|
|
66886
|
+
refused: (dynamic.redirected ?? []).map((r) => ({ id: r.id, reason: r.reason }))
|
|
66887
|
+
};
|
|
66888
|
+
}
|
|
66882
66889
|
function mergeDynamic(audit2, dynamic, lang = "en") {
|
|
66883
66890
|
const merged = JSON.parse(JSON.stringify(audit2));
|
|
66884
66891
|
const seenFindings = new Set(merged.findings.map(portableFindingId));
|
|
@@ -74160,9 +74167,9 @@ Usage:
|
|
|
74160
74167
|
ultra11y init [--hook] [--ci] [--baseline] [--fail-on blocking|major|minor]
|
|
74161
74168
|
ultra11y judge --in <audit.json> [--standard <pack>] [--runner api|claude|codex] [--grain batch|criterion] [--max <n>] [--model <id>] [--effort <level>] [--max-budget-usd <n>] [--timeout <s>] [--out <dir>] [--apply] (adjudicate manual criteria \u2014 api needs ANTHROPIC_API_KEY; claude/codex reuse their CLI login; cli aliases claude)
|
|
74162
74169
|
ultra11y pack check <pack.json> [--guidance <g.json>] [--json] | pack scaffold
|
|
74163
|
-
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74164
|
-
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74165
|
-
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--merge <audit.json>] [--json]
|
|
74170
|
+
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74171
|
+
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74172
|
+
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--merge <audit.json>] [--json]
|
|
74166
74173
|
ultra11y scan --clean (remove the dynamic-tier Docker image + temp contexts)
|
|
74167
74174
|
ultra11y sample check [--standard <pack>] [--json] (lint the .ultra11yrc.json page sample vs the standard's required page kinds)
|
|
74168
74175
|
ultra11y snapshot write [--root <dir>] [--fail-on blocking|major|minor] [--json] (payload on stdin \u2192 .ultra11y/pages/<id>/ + audit it)
|
|
@@ -74406,7 +74413,8 @@ Options:
|
|
|
74406
74413
|
--no-captures audit: do NOT auto-detect/ingest .ultra11y/captures nor .ultra11y/pages
|
|
74407
74414
|
--e2e render: write the Playwright/Cypress fixtures into .ultra11y/e2e/
|
|
74408
74415
|
--runner <name> render --e2e: force playwright|cypress instead of auto-detecting
|
|
74409
|
-
--root <dir> snapshot: project root holding .ultra11y/pages (default: .
|
|
74416
|
+
--root <dir> snapshot, scan: project root holding .ultra11y/pages (default: . \u2014
|
|
74417
|
+
scan writes the snapshots it collects under it)
|
|
74410
74418
|
--port <n> dev: port for the side-car (default: 4111); mcp: HTTP transport port
|
|
74411
74419
|
--transport <k> mcp: stdio (default) or http
|
|
74412
74420
|
--bind <addr> mcp --transport http: address to bind (default 127.0.0.1)
|
|
@@ -74492,7 +74500,9 @@ Options:
|
|
|
74492
74500
|
(exit 2 with the producing command if its worklist is missing)
|
|
74493
74501
|
--eco orchestrate: emit only RUNBOOK.md + agents/*.md \u2014 the explicit
|
|
74494
74502
|
sequential low-token path (also what a no-subagent harness follows)
|
|
74495
|
-
--merge <file> scan: fold dynamic findings into this AuditResult JSON
|
|
74503
|
+
--merge <file> scan: fold dynamic findings into this AuditResult JSON. With --json the
|
|
74504
|
+
printed document opens with a 'pages' summary of the run \u2014
|
|
74505
|
+
{sample, recorded, refused:[{id, reason}]} \u2014 beside the document
|
|
74496
74506
|
--sitemap <url> scan: scan every URL listed in a sitemap.xml
|
|
74497
74507
|
--crawl <url> scan: BFS same-origin links from a start URL (served HTML)
|
|
74498
74508
|
--depth <n> scan: crawl link-hop depth from the start URL (default: no limit; 0 = no limit)
|
|
@@ -77031,6 +77041,10 @@ Iterated over ${rounds} round(s) to a fixpoint \u2014 ${totalWritten} file(s) wr
|
|
|
77031
77041
|
}
|
|
77032
77042
|
return 0;
|
|
77033
77043
|
}
|
|
77044
|
+
function scanSnapshotRoot(flags2) {
|
|
77045
|
+
if (flags2["no-snapshot"] === true) return void 0;
|
|
77046
|
+
return typeof flags2.root === "string" && flags2.root ? resolve17(flags2.root) : process.cwd();
|
|
77047
|
+
}
|
|
77034
77048
|
async function cmdScan(p) {
|
|
77035
77049
|
const standard = stdOf(p, "scan");
|
|
77036
77050
|
if (standard === null) return 2;
|
|
@@ -77083,7 +77097,7 @@ async function cmdScan(p) {
|
|
|
77083
77097
|
const interactClicks = p.flags["interact-clicks"] === true;
|
|
77084
77098
|
const sitemap = typeof p.flags.sitemap === "string" ? p.flags.sitemap : void 0;
|
|
77085
77099
|
const crawl = typeof p.flags.crawl === "string" ? p.flags.crawl : void 0;
|
|
77086
|
-
const snapshotRoot = p.flags
|
|
77100
|
+
const snapshotRoot = scanSnapshotRoot(p.flags);
|
|
77087
77101
|
const useSample = p.flags.sample === true;
|
|
77088
77102
|
let sampleConfig;
|
|
77089
77103
|
if (useSample) {
|
|
@@ -77211,7 +77225,19 @@ async function cmdScan(p) {
|
|
|
77211
77225
|
mkdirSync18(out2, { recursive: true });
|
|
77212
77226
|
const mergedDocument = auditDocumentFor(merged, standard, lang);
|
|
77213
77227
|
writeFileSync21(join52(out2, "audit-latest.json"), JSON.stringify(mergedDocument, null, 2) + "\n");
|
|
77214
|
-
if (p.flags.json)
|
|
77228
|
+
if (p.flags.json)
|
|
77229
|
+
console.log(
|
|
77230
|
+
JSON.stringify(
|
|
77231
|
+
// `pages` opens the printed document: what this scan recorded (the
|
|
77232
|
+
// .ultra11y/pages ids) and refused, which the stderr warnings alone don't make
|
|
77233
|
+
// countable. An extra top-level key the `--in` readers never look at, so the
|
|
77234
|
+
// document beside it stays pipeable into report/verify — and the file above
|
|
77235
|
+
// keeps the plain audit schema.
|
|
77236
|
+
{ pages: scanPageSummary(dynamic, sampleConfig?.pages.length), ...mergedDocument },
|
|
77237
|
+
null,
|
|
77238
|
+
2
|
|
77239
|
+
)
|
|
77240
|
+
);
|
|
77215
77241
|
else {
|
|
77216
77242
|
console.log(
|
|
77217
77243
|
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
|
|
@@ -77768,6 +77794,7 @@ export {
|
|
|
77768
77794
|
COMMANDS,
|
|
77769
77795
|
REMOVED_FLAGS,
|
|
77770
77796
|
main,
|
|
77771
|
-
parseArgs
|
|
77797
|
+
parseArgs,
|
|
77798
|
+
scanSnapshotRoot
|
|
77772
77799
|
};
|
|
77773
77800
|
// "Copyright" and "@license" are already caught by DIRECTIVE_RE.
|
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: ultra11y
|
|
|
3
3
|
description: "Use to AUDIT a repository, site, or page against WCAG 2.2 AA or a country standard such as RGAA; produce a dated conformance report, criterion grid, PRD backlog, or tickets; AUTHOR accessible markup; fix accessibility; or wire a repo gate and rendered-DOM capture pipeline. The bundled install-free engine runs 93 static checks tied to success criteria, while the agent adjudicates judgment criteria and routes rendering criteria to browser scans; check/verify reject invented non-conformities. Use `review-a11y` instead for a review scoped only to a diff, branch, or PR."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: 5.
|
|
6
|
+
version: 5.45.0
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -233,7 +233,8 @@ and the scan stops there. Point `--crawl` at a real entry page, or pass the URLs
|
|
|
233
233
|
`scan` does not only keep findings: each page it visits is persisted to
|
|
234
234
|
`.ultra11y/pages/<id>/` (DOM + computed styles + boxes + stylesheets + a viewport
|
|
235
235
|
screenshot). The browser is already on the page, so this costs one `evaluate`. `--no-snapshot`
|
|
236
|
-
opts out
|
|
236
|
+
opts out; `--root <dir>` relocates the tree (default: the working directory — `--cwd` is
|
|
237
|
+
where Playwright resolves from, not where snapshots land).
|
|
237
238
|
|
|
238
239
|
### …and what it MEASURED, not only what it saw
|
|
239
240
|
|
|
@@ -166,9 +166,10 @@ repo it does not own — write the run's intermediates out-of-tree and let only
|
|
|
166
166
|
dated report land in the project, if at all.
|
|
167
167
|
|
|
168
168
|
`.ultra11y/pages/` is written only by the page-snapshot commands (`scan`, `snapshot`,
|
|
169
|
-
`dev`).
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
`dev`). All of them take `--root` to relocate it — for `scan`, where the snapshots it
|
|
170
|
+
collects are written (`--cwd` stays where Playwright resolves from), so an orchestrator can
|
|
171
|
+
run from the audited checkout without dirtying it. `audit`, `report`, `prd`, `tickets`,
|
|
172
|
+
`verify` and `check` never touch it.
|
|
172
173
|
|
|
173
174
|
## Exit codes
|
|
174
175
|
|
|
@@ -207,6 +208,12 @@ Read exit codes from the process, **not** from a pipeline — `cmd --json | head
|
|
|
207
208
|
standard's own conformance rate (validated ÷ validated + NC + à évaluer, NA excluded) is
|
|
208
209
|
printed by the report; derive it from `criteria[]` if a number is needed in a pipeline.
|
|
209
210
|
- `check --json` → `{ ok, issues[] }`.
|
|
211
|
+
- `scan --merge --json` → the merged audit document, opened by a top-level `pages` summary:
|
|
212
|
+
`{ sample, recorded[], refused: [{id, reason}] }` — the declared sample's size (`--sample`),
|
|
213
|
+
the snapshot ids written under `.ultra11y/pages/`, and the pages the scan refused with
|
|
214
|
+
their reason (the refusal warnings also go to stderr). Report the rendered layer from it —
|
|
215
|
+
captured vs refused — instead of re-deriving it from directory mtimes. The document written
|
|
216
|
+
to `--out` keeps the plain audit schema; the summary travels in stdout only.
|
|
210
217
|
|
|
211
218
|
## Honesty rules an orchestrator has to carry
|
|
212
219
|
|
|
@@ -6,7 +6,7 @@ import { join as join52, relative as relative6, resolve as resolve17, 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.45.0";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -66879,6 +66879,13 @@ function resolveHostAnchor(file, snippet2) {
|
|
|
66879
66879
|
}
|
|
66880
66880
|
return null;
|
|
66881
66881
|
}
|
|
66882
|
+
function scanPageSummary(dynamic, sampleSize) {
|
|
66883
|
+
return {
|
|
66884
|
+
...sampleSize === void 0 ? {} : { sample: sampleSize },
|
|
66885
|
+
recorded: dynamic.snapshots ?? [],
|
|
66886
|
+
refused: (dynamic.redirected ?? []).map((r) => ({ id: r.id, reason: r.reason }))
|
|
66887
|
+
};
|
|
66888
|
+
}
|
|
66882
66889
|
function mergeDynamic(audit2, dynamic, lang = "en") {
|
|
66883
66890
|
const merged = JSON.parse(JSON.stringify(audit2));
|
|
66884
66891
|
const seenFindings = new Set(merged.findings.map(portableFindingId));
|
|
@@ -74160,9 +74167,9 @@ Usage:
|
|
|
74160
74167
|
ultra11y init [--hook] [--ci] [--baseline] [--fail-on blocking|major|minor]
|
|
74161
74168
|
ultra11y judge --in <audit.json> [--standard <pack>] [--runner api|claude|codex] [--grain batch|criterion] [--max <n>] [--model <id>] [--effort <level>] [--max-budget-usd <n>] [--timeout <s>] [--out <dir>] [--apply] (adjudicate manual criteria \u2014 api needs ANTHROPIC_API_KEY; claude/codex reuse their CLI login; cli aliases claude)
|
|
74162
74169
|
ultra11y pack check <pack.json> [--guidance <g.json>] [--json] | pack scaffold
|
|
74163
|
-
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74164
|
-
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74165
|
-
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--merge <audit.json>] [--json]
|
|
74170
|
+
ultra11y scan <url|file\u2026> [--runtime auto|local|docker] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
74171
|
+
ultra11y scan --sample [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
74172
|
+
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime \u2026] [--cwd <dir>] [--root <dir>] [--merge <audit.json>] [--json]
|
|
74166
74173
|
ultra11y scan --clean (remove the dynamic-tier Docker image + temp contexts)
|
|
74167
74174
|
ultra11y sample check [--standard <pack>] [--json] (lint the .ultra11yrc.json page sample vs the standard's required page kinds)
|
|
74168
74175
|
ultra11y snapshot write [--root <dir>] [--fail-on blocking|major|minor] [--json] (payload on stdin \u2192 .ultra11y/pages/<id>/ + audit it)
|
|
@@ -74406,7 +74413,8 @@ Options:
|
|
|
74406
74413
|
--no-captures audit: do NOT auto-detect/ingest .ultra11y/captures nor .ultra11y/pages
|
|
74407
74414
|
--e2e render: write the Playwright/Cypress fixtures into .ultra11y/e2e/
|
|
74408
74415
|
--runner <name> render --e2e: force playwright|cypress instead of auto-detecting
|
|
74409
|
-
--root <dir> snapshot: project root holding .ultra11y/pages (default: .
|
|
74416
|
+
--root <dir> snapshot, scan: project root holding .ultra11y/pages (default: . \u2014
|
|
74417
|
+
scan writes the snapshots it collects under it)
|
|
74410
74418
|
--port <n> dev: port for the side-car (default: 4111); mcp: HTTP transport port
|
|
74411
74419
|
--transport <k> mcp: stdio (default) or http
|
|
74412
74420
|
--bind <addr> mcp --transport http: address to bind (default 127.0.0.1)
|
|
@@ -74492,7 +74500,9 @@ Options:
|
|
|
74492
74500
|
(exit 2 with the producing command if its worklist is missing)
|
|
74493
74501
|
--eco orchestrate: emit only RUNBOOK.md + agents/*.md \u2014 the explicit
|
|
74494
74502
|
sequential low-token path (also what a no-subagent harness follows)
|
|
74495
|
-
--merge <file> scan: fold dynamic findings into this AuditResult JSON
|
|
74503
|
+
--merge <file> scan: fold dynamic findings into this AuditResult JSON. With --json the
|
|
74504
|
+
printed document opens with a 'pages' summary of the run \u2014
|
|
74505
|
+
{sample, recorded, refused:[{id, reason}]} \u2014 beside the document
|
|
74496
74506
|
--sitemap <url> scan: scan every URL listed in a sitemap.xml
|
|
74497
74507
|
--crawl <url> scan: BFS same-origin links from a start URL (served HTML)
|
|
74498
74508
|
--depth <n> scan: crawl link-hop depth from the start URL (default: no limit; 0 = no limit)
|
|
@@ -77031,6 +77041,10 @@ Iterated over ${rounds} round(s) to a fixpoint \u2014 ${totalWritten} file(s) wr
|
|
|
77031
77041
|
}
|
|
77032
77042
|
return 0;
|
|
77033
77043
|
}
|
|
77044
|
+
function scanSnapshotRoot(flags2) {
|
|
77045
|
+
if (flags2["no-snapshot"] === true) return void 0;
|
|
77046
|
+
return typeof flags2.root === "string" && flags2.root ? resolve17(flags2.root) : process.cwd();
|
|
77047
|
+
}
|
|
77034
77048
|
async function cmdScan(p) {
|
|
77035
77049
|
const standard = stdOf(p, "scan");
|
|
77036
77050
|
if (standard === null) return 2;
|
|
@@ -77083,7 +77097,7 @@ async function cmdScan(p) {
|
|
|
77083
77097
|
const interactClicks = p.flags["interact-clicks"] === true;
|
|
77084
77098
|
const sitemap = typeof p.flags.sitemap === "string" ? p.flags.sitemap : void 0;
|
|
77085
77099
|
const crawl = typeof p.flags.crawl === "string" ? p.flags.crawl : void 0;
|
|
77086
|
-
const snapshotRoot = p.flags
|
|
77100
|
+
const snapshotRoot = scanSnapshotRoot(p.flags);
|
|
77087
77101
|
const useSample = p.flags.sample === true;
|
|
77088
77102
|
let sampleConfig;
|
|
77089
77103
|
if (useSample) {
|
|
@@ -77211,7 +77225,19 @@ async function cmdScan(p) {
|
|
|
77211
77225
|
mkdirSync18(out2, { recursive: true });
|
|
77212
77226
|
const mergedDocument = auditDocumentFor(merged, standard, lang);
|
|
77213
77227
|
writeFileSync21(join52(out2, "audit-latest.json"), JSON.stringify(mergedDocument, null, 2) + "\n");
|
|
77214
|
-
if (p.flags.json)
|
|
77228
|
+
if (p.flags.json)
|
|
77229
|
+
console.log(
|
|
77230
|
+
JSON.stringify(
|
|
77231
|
+
// `pages` opens the printed document: what this scan recorded (the
|
|
77232
|
+
// .ultra11y/pages ids) and refused, which the stderr warnings alone don't make
|
|
77233
|
+
// countable. An extra top-level key the `--in` readers never look at, so the
|
|
77234
|
+
// document beside it stays pipeable into report/verify — and the file above
|
|
77235
|
+
// keeps the plain audit schema.
|
|
77236
|
+
{ pages: scanPageSummary(dynamic, sampleConfig?.pages.length), ...mergedDocument },
|
|
77237
|
+
null,
|
|
77238
|
+
2
|
|
77239
|
+
)
|
|
77240
|
+
);
|
|
77215
77241
|
else {
|
|
77216
77242
|
console.log(
|
|
77217
77243
|
lang === "fr" ? `Audit statique + dynamique fusionn\xE9 \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% r\xE9ussite, ${merged.findings.length} findings).` : `Static + dynamic audit merged \u2192 ${join52(out2, "audit-latest.json")} (${merged.conformancePct}% pass rate, ${merged.findings.length} findings).`
|
|
@@ -77768,6 +77794,7 @@ export {
|
|
|
77768
77794
|
COMMANDS,
|
|
77769
77795
|
REMOVED_FLAGS,
|
|
77770
77796
|
main,
|
|
77771
|
-
parseArgs
|
|
77797
|
+
parseArgs,
|
|
77798
|
+
scanSnapshotRoot
|
|
77772
77799
|
};
|
|
77773
77800
|
// "Copyright" and "@license" are already caught by DIRECTIVE_RE.
|
|
@@ -27,7 +27,7 @@ variables:
|
|
|
27
27
|
ULTRA11Y_LANG: en
|
|
28
28
|
# Exact by default: a CI report must say which engine produced it. The release script keeps
|
|
29
29
|
# this pin aligned with the package version; override only for a deliberate upgrade test.
|
|
30
|
-
ULTRA11Y_VERSION: '5.
|
|
30
|
+
ULTRA11Y_VERSION: '5.45.0'
|
|
31
31
|
# Batches of 8 amortise the prompt; each accepted verdict is checkpointed before the next
|
|
32
32
|
# batch, and later passes receive only the residue.
|
|
33
33
|
ULTRA11Y_GRAIN: batch
|
package/src/cli.ts
CHANGED
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
import { DEFAULT_CLI_MODEL, EFFORT_LEVELS, judgeBatchCli, refuteBatchCli } from "./agent-cli.js";
|
|
83
83
|
import { CODEX_EFFORT_LEVELS, judgeBatchCodex, refuteBatchCodex } from "./agent-codex.js";
|
|
84
84
|
import { BATCH_SIZE, apiKeyFromEnv, applyRawVerdicts, batchWorklist, isProviderUnavailableError, judgeAll, modelFromEnv, type LlmOptions } from "./llm.js";
|
|
85
|
-
import { runScan, runScanMany, runCrawlScan, runSampleScan, mergeDynamic, mergeSnapshotAudit, cleanDynamic, dockerAvailable } from "./scan.js";
|
|
85
|
+
import { runScan, runScanMany, runCrawlScan, runSampleScan, mergeDynamic, mergeSnapshotAudit, cleanDynamic, dockerAvailable, scanPageSummary } from "./scan.js";
|
|
86
86
|
import { runScanLocal, runScanManyLocal, runCrawlScanLocal, runSampleScanLocal, localAvailable, localTierStatus } from "./scan-local.js";
|
|
87
87
|
import { validateSample, lintSample, kindLabel, proposeSamplePages, mergeSample, sampleFromSnapshots, unionSample } from "./sample.js";
|
|
88
88
|
import { createAdapter } from "./external/registry.js";
|
|
@@ -160,9 +160,9 @@ Usage:
|
|
|
160
160
|
ultra11y init [--hook] [--ci] [--baseline] [--fail-on blocking|major|minor]
|
|
161
161
|
ultra11y judge --in <audit.json> [--standard <pack>] [--runner api|claude|codex] [--grain batch|criterion] [--max <n>] [--model <id>] [--effort <level>] [--max-budget-usd <n>] [--timeout <s>] [--out <dir>] [--apply] (adjudicate manual criteria — api needs ANTHROPIC_API_KEY; claude/codex reuse their CLI login; cli aliases claude)
|
|
162
162
|
ultra11y pack check <pack.json> [--guidance <g.json>] [--json] | pack scaffold
|
|
163
|
-
ultra11y scan <url|file…> [--runtime auto|local|docker] [--cwd <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
164
|
-
ultra11y scan --sample [--runtime …] [--cwd <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
165
|
-
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime …] [--cwd <dir>] [--merge <audit.json>] [--json]
|
|
163
|
+
ultra11y scan <url|file…> [--runtime auto|local|docker] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--no-interact] [--interact-clicks] [--no-snapshot] [--merge <audit.json>] [--out <dir>] [--json]
|
|
164
|
+
ultra11y scan --sample [--runtime …] [--cwd <dir>] [--root <dir>] [--storage-state <file>] [--merge <audit.json>] [--json] (scan the .ultra11yrc.json page sample)
|
|
165
|
+
ultra11y scan --sitemap <url> | --crawl <url> [--depth <n>] [--max <n>] [--runtime …] [--cwd <dir>] [--root <dir>] [--merge <audit.json>] [--json]
|
|
166
166
|
ultra11y scan --clean (remove the dynamic-tier Docker image + temp contexts)
|
|
167
167
|
ultra11y sample check [--standard <pack>] [--json] (lint the .ultra11yrc.json page sample vs the standard's required page kinds)
|
|
168
168
|
ultra11y snapshot write [--root <dir>] [--fail-on blocking|major|minor] [--json] (payload on stdin → .ultra11y/pages/<id>/ + audit it)
|
|
@@ -406,7 +406,8 @@ Options:
|
|
|
406
406
|
--no-captures audit: do NOT auto-detect/ingest .ultra11y/captures nor .ultra11y/pages
|
|
407
407
|
--e2e render: write the Playwright/Cypress fixtures into .ultra11y/e2e/
|
|
408
408
|
--runner <name> render --e2e: force playwright|cypress instead of auto-detecting
|
|
409
|
-
--root <dir> snapshot: project root holding .ultra11y/pages (default: .
|
|
409
|
+
--root <dir> snapshot, scan: project root holding .ultra11y/pages (default: . —
|
|
410
|
+
scan writes the snapshots it collects under it)
|
|
410
411
|
--port <n> dev: port for the side-car (default: 4111); mcp: HTTP transport port
|
|
411
412
|
--transport <k> mcp: stdio (default) or http
|
|
412
413
|
--bind <addr> mcp --transport http: address to bind (default 127.0.0.1)
|
|
@@ -492,7 +493,9 @@ Options:
|
|
|
492
493
|
(exit 2 with the producing command if its worklist is missing)
|
|
493
494
|
--eco orchestrate: emit only RUNBOOK.md + agents/*.md — the explicit
|
|
494
495
|
sequential low-token path (also what a no-subagent harness follows)
|
|
495
|
-
--merge <file> scan: fold dynamic findings into this AuditResult JSON
|
|
496
|
+
--merge <file> scan: fold dynamic findings into this AuditResult JSON. With --json the
|
|
497
|
+
printed document opens with a 'pages' summary of the run —
|
|
498
|
+
{sample, recorded, refused:[{id, reason}]} — beside the document
|
|
496
499
|
--sitemap <url> scan: scan every URL listed in a sitemap.xml
|
|
497
500
|
--crawl <url> scan: BFS same-origin links from a start URL (served HTML)
|
|
498
501
|
--depth <n> scan: crawl link-hop depth from the start URL (default: no limit; 0 = no limit)
|
|
@@ -4028,6 +4031,16 @@ async function cmdFix(p: ParsedArgs): Promise<number> {
|
|
|
4028
4031
|
return 0;
|
|
4029
4032
|
}
|
|
4030
4033
|
|
|
4034
|
+
/** Where `scan` writes the page snapshots it collects: `--root <dir>` when given — the same
|
|
4035
|
+
* contract as `snapshot write`, `dev` and `pages discover --from-snapshots` — else the
|
|
4036
|
+
* working directory. Absolute either way, as it always was: the merge audits the snapshot
|
|
4037
|
+
* DOMs by this path. Undefined under `--no-snapshot`: nothing is persisted. `--cwd` plays
|
|
4038
|
+
* no part here: it keeps its own meaning, where Playwright resolves from. */
|
|
4039
|
+
export function scanSnapshotRoot(flags: ParsedArgs["flags"]): string | undefined {
|
|
4040
|
+
if (flags["no-snapshot"] === true) return undefined;
|
|
4041
|
+
return typeof flags.root === "string" && flags.root ? resolve(flags.root) : process.cwd();
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4031
4044
|
async function cmdScan(p: ParsedArgs): Promise<number> {
|
|
4032
4045
|
// `scan` takes `--standard` for the same reason `audit` now does: it REWRITES
|
|
4033
4046
|
// audit-latest.json mid-chain, and a merge that silently re-keyed an RGAA document back to
|
|
@@ -4135,7 +4148,7 @@ async function cmdScan(p: ParsedArgs): Promise<number> {
|
|
|
4135
4148
|
// is already on the page, so this costs one `evaluate` — and it is what lets the page be
|
|
4136
4149
|
// re-audited offline and earn a real per-page verdict instead of staying "to assess"
|
|
4137
4150
|
// forever (src/pages.ts). `--no-snapshot` opts out.
|
|
4138
|
-
const snapshotRoot = p.flags
|
|
4151
|
+
const snapshotRoot = scanSnapshotRoot(p.flags);
|
|
4139
4152
|
|
|
4140
4153
|
// --sample: iterate the NORMATIVE page sample from `.ultra11yrc.json` (per-page
|
|
4141
4154
|
// storageState overrides --storage-state). Loaded + validated here (hard error on a
|
|
@@ -4336,7 +4349,19 @@ async function cmdScan(p: ParsedArgs): Promise<number> {
|
|
|
4336
4349
|
mkdirSync(out, { recursive: true });
|
|
4337
4350
|
const mergedDocument = auditDocumentFor(merged, standard, lang);
|
|
4338
4351
|
writeFileSync(join(out, "audit-latest.json"), JSON.stringify(mergedDocument, null, 2) + "\n");
|
|
4339
|
-
if (p.flags.json)
|
|
4352
|
+
if (p.flags.json)
|
|
4353
|
+
console.log(
|
|
4354
|
+
JSON.stringify(
|
|
4355
|
+
// `pages` opens the printed document: what this scan recorded (the
|
|
4356
|
+
// .ultra11y/pages ids) and refused, which the stderr warnings alone don't make
|
|
4357
|
+
// countable. An extra top-level key the `--in` readers never look at, so the
|
|
4358
|
+
// document beside it stays pipeable into report/verify — and the file above
|
|
4359
|
+
// keeps the plain audit schema.
|
|
4360
|
+
{ pages: scanPageSummary(dynamic, sampleConfig?.pages.length), ...mergedDocument },
|
|
4361
|
+
null,
|
|
4362
|
+
2,
|
|
4363
|
+
),
|
|
4364
|
+
);
|
|
4340
4365
|
else {
|
|
4341
4366
|
console.log(
|
|
4342
4367
|
lang === "fr"
|
package/src/scan.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { mkdtempSync, writeFileSync, existsSync, statSync, readdirSync, rmSync,
|
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { join, resolve } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
import type { AuditResult, DynamicEngine, DynamicFinding, DynamicResult, Finding, Lang, SamplePage, Severity } from "./types.js";
|
|
13
|
+
import type { AuditResult, DynamicEngine, DynamicFinding, DynamicResult, Finding, Lang, SamplePage, ScanRedirect, Severity } from "./types.js";
|
|
14
14
|
import { lineStartsOf, lineColAt } from "./parse/html.js";
|
|
15
15
|
import { allGuidelines } from "./wcag.js";
|
|
16
16
|
import { PROBE_SEVERITY, PROBE_WCAG, scForAxe, severityFromImpact, isAxeAdvisory } from "./axe-map.js";
|
|
@@ -716,6 +716,31 @@ function resolveHostAnchor(file: string, snippet: string): { line: number; col:
|
|
|
716
716
|
return null;
|
|
717
717
|
}
|
|
718
718
|
|
|
719
|
+
/** The `pages` block `scan --merge --json` prints in front of the merged document. A
|
|
720
|
+
* PRINT-side envelope only — `audit-latest.json` keeps the plain audit schema — under a
|
|
721
|
+
* top-level key no `--in` reader of the document looks at. */
|
|
722
|
+
export interface ScanPageSummary {
|
|
723
|
+
// The declared sample's page count, on a `--sample` run; absent otherwise (a plain
|
|
724
|
+
// URL/sitemap/crawl scan has no sample to be short of).
|
|
725
|
+
sample?: number;
|
|
726
|
+
// Ids of the snapshots the run persisted under `.ultra11y/pages/<id>/`.
|
|
727
|
+
recorded: string[];
|
|
728
|
+
// Pages the scan refused to record, and why (see ScanRedirect).
|
|
729
|
+
refused: Array<{ id: string; reason?: ScanRedirect["reason"] }>;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** What one scan RECORDED and REFUSED, page by page. Without it the merged `--json` output
|
|
733
|
+
* carries neither: the refusals reach the caller on stderr only, and the recorded ids would
|
|
734
|
+
* take a listing of `.ultra11y/pages` against the run's start time to recover — exactly the
|
|
735
|
+
* re-derivation an orchestrator reporting « N pages capturées, M refusées » must not do. */
|
|
736
|
+
export function scanPageSummary(dynamic: DynamicResult, sampleSize?: number): ScanPageSummary {
|
|
737
|
+
return {
|
|
738
|
+
...(sampleSize === undefined ? {} : { sample: sampleSize }),
|
|
739
|
+
recorded: dynamic.snapshots ?? [],
|
|
740
|
+
refused: (dynamic.redirected ?? []).map((r) => ({ id: r.id, reason: r.reason })),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
719
744
|
/** Fold dynamic findings into a static AuditResult: a needs-rendering/manual or
|
|
720
745
|
* clean criterion that axe flags becomes NC; tallies + conformance recompute. */
|
|
721
746
|
export function mergeDynamic(audit: AuditResult, dynamic: DynamicResult, lang: Lang = "en"): AuditResult {
|
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.45.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,
|