a11ign 0.1.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 +661 -0
- package/README.md +140 -0
- package/dist/action/post-comment.d.ts +20 -0
- package/dist/action/post-comment.d.ts.map +1 -0
- package/dist/action/post-comment.js +93 -0
- package/dist/action/post-comment.js.map +1 -0
- package/dist/action/run.d.ts +2 -0
- package/dist/action/run.d.ts.map +1 -0
- package/dist/action/run.js +119 -0
- package/dist/action/run.js.map +1 -0
- package/dist/action/summary.d.ts +180 -0
- package/dist/action/summary.d.ts.map +1 -0
- package/dist/action/summary.js +355 -0
- package/dist/action/summary.js.map +1 -0
- package/dist/cli.d.ts +279 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1028 -0
- package/dist/cli.js.map +1 -0
- package/dist/fault-remediation.d.ts +87 -0
- package/dist/fault-remediation.d.ts.map +1 -0
- package/dist/fault-remediation.js +156 -0
- package/dist/fault-remediation.js.map +1 -0
- package/dist/forms/config.d.ts +69 -0
- package/dist/forms/config.d.ts.map +1 -0
- package/dist/forms/config.js +185 -0
- package/dist/forms/config.js.map +1 -0
- package/dist/forms/coverage.d.ts +51 -0
- package/dist/forms/coverage.d.ts.map +1 -0
- package/dist/forms/coverage.js +80 -0
- package/dist/forms/coverage.js.map +1 -0
- package/dist/forms/draft.d.ts +62 -0
- package/dist/forms/draft.d.ts.map +1 -0
- package/dist/forms/draft.js +165 -0
- package/dist/forms/draft.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/report.d.ts +48 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +322 -0
- package/dist/report.js.map +1 -0
- package/dist/scan/axe-results.d.ts +25 -0
- package/dist/scan/axe-results.d.ts.map +1 -0
- package/dist/scan/axe-results.js +111 -0
- package/dist/scan/axe-results.js.map +1 -0
- package/dist/scan/axe.d.ts +158 -0
- package/dist/scan/axe.d.ts.map +1 -0
- package/dist/scan/axe.js +203 -0
- package/dist/scan/axe.js.map +1 -0
- package/dist/scan/page-title.d.ts +3 -0
- package/dist/scan/page-title.d.ts.map +1 -0
- package/dist/scan/page-title.js +41 -0
- package/dist/scan/page-title.js.map +1 -0
- package/dist/scan/run-axe.d.ts +2 -0
- package/dist/scan/run-axe.d.ts.map +1 -0
- package/dist/scan/run-axe.js +39 -0
- package/dist/scan/run-axe.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a witness run as Markdown for a GitHub job summary or PR comment, and decide pass/fail.
|
|
3
|
+
*
|
|
4
|
+
* Kept pure and separate from the Action's YAML so the output is testable without pushing a commit and
|
|
5
|
+
* waiting for a Windows runner. That matters more here than usual: the whole point of the Action is to
|
|
6
|
+
* tell someone their page has an accessibility problem, so a renderer that drops a finding, or a gate
|
|
7
|
+
* that passes when it should fail, is worse than no Action at all.
|
|
8
|
+
*/
|
|
9
|
+
/** #1366: absent means `secondary`, so an unmapped finding never reads as an assertion (`report.ts` reads it the same way). */
|
|
10
|
+
function isReferral(finding) {
|
|
11
|
+
return finding.mapping !== "conformance";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The conformance scope's own sentence for a capture whose marks named more than one document -- #1387.
|
|
15
|
+
*
|
|
16
|
+
* READ, never rephrased: `@a11ign/evidence`'s `document-identity.ts` writes it into Requirement 2, and the
|
|
17
|
+
* JSON already carries it, so the summary quotes what the evidence layer concluded rather than deciding again.
|
|
18
|
+
* Rehearsals 3 and 5 each named two documents, and only a reader who opened the artifact could find out.
|
|
19
|
+
* `summary.test.ts` takes the wording from the real producer, so a change to it fails there rather than here
|
|
20
|
+
* going quiet. Null when no requirement carries it, including a result with no conformance at all.
|
|
21
|
+
*/
|
|
22
|
+
const DOCUMENTS_SPANNED = /THIS CAPTURE NAMED MORE THAN ONE DOCUMENT.*?more than one page\./;
|
|
23
|
+
export function documentsSpannedSentence(conformance) {
|
|
24
|
+
for (const requirement of conformance ?? []) {
|
|
25
|
+
const found = requirement.limitation?.match(DOCUMENTS_SPANNED);
|
|
26
|
+
if (found)
|
|
27
|
+
return found[0];
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The producer's own sentences for a criterion resting on an examination that stopped short -- #1563.
|
|
33
|
+
*
|
|
34
|
+
* READ, never rephrased, like `DOCUMENTS_SPANNED` above: `@a11ign/judge`'s `outcomes.ts` ends a `cantTell` reason
|
|
35
|
+
* with one of these when a sweep feeding the criterion stopped before the page did, or ended having reached less
|
|
36
|
+
* than the browser exposes. Rehearsal 2's result carried eight, and its log read `1 finding(s)` with no word of
|
|
37
|
+
* them. `summary.test.ts` drives the real producer for both sentences, so a change to its wording fails there
|
|
38
|
+
* rather than this count going quietly to zero. Left out: the left-site reason, which #1363's own line states.
|
|
39
|
+
*/
|
|
40
|
+
const PARTIAL_EXAMINATION = new RegExp("(?:so this criterion rests on an examination known to be partial"
|
|
41
|
+
+ "|sweep stopped before the page did, so content past that point was never examined for this criterion)\\.$");
|
|
42
|
+
/** How many undetermined criteria rest on an examination known to be partial. Zero with no outcomes at all. */
|
|
43
|
+
export function partialExaminationCount(outcomes) {
|
|
44
|
+
return (outcomes ?? []).filter((o) => o.outcome === "cantTell" && PARTIAL_EXAMINATION.test(o.reason)).length;
|
|
45
|
+
}
|
|
46
|
+
/** Ordered worst-first, so a threshold can be "this severity or worse". */
|
|
47
|
+
const SEVERITY_ORDER = ["blocker", "serious", "moderate", "minor"];
|
|
48
|
+
/** #1618: `ceo`'s wording, stated wherever fail-on is: the input's description, the Action guide, and the log when it can decide the exit. */
|
|
49
|
+
const FAIL_ON_RULE = "fail-on counts asserted findings; referrals are listed and never fail the run";
|
|
50
|
+
/**
|
|
51
|
+
* Should this run fail the check?
|
|
52
|
+
*
|
|
53
|
+
* `never` is the default deliberately. A tool that starts failing builds the day it is installed gets
|
|
54
|
+
* uninstalled; one that reports first, and fails when the team asks it to, gets adopted. The same
|
|
55
|
+
* reasoning is why the rule layer is opt-out rather than mandatory.
|
|
56
|
+
*/
|
|
57
|
+
export function shouldFail(findings, failOn) {
|
|
58
|
+
if (failOn === "never")
|
|
59
|
+
return false;
|
|
60
|
+
// #1618, `ceo`'s ruling (a): only an ASSERTED finding can fail the run. A referral is by definition a person's decision,
|
|
61
|
+
// and a threshold that fired on its severity made the tool assert what the judge had refused to.
|
|
62
|
+
const assertedFindings = findings.filter((f) => !isReferral(f));
|
|
63
|
+
if (failOn === "any")
|
|
64
|
+
return assertedFindings.length > 0;
|
|
65
|
+
const threshold = SEVERITY_ORDER.indexOf(failOn);
|
|
66
|
+
// An unrecognised threshold must not silently mean "never fail" -- that would turn a typo in a
|
|
67
|
+
// workflow file into a check that always passes, which is the failure mode nobody notices.
|
|
68
|
+
if (threshold === -1)
|
|
69
|
+
throw new Error(`unknown fail-on value ${JSON.stringify(failOn)}`);
|
|
70
|
+
return assertedFindings.some((f) => {
|
|
71
|
+
const rank = SEVERITY_ORDER.indexOf(f.severity);
|
|
72
|
+
return rank !== -1 && rank <= threshold;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const ICON = {
|
|
76
|
+
blocker: "🛑", serious: "🔴", moderate: "🟠", minor: "🟡",
|
|
77
|
+
};
|
|
78
|
+
/** Sort worst-first so the most important finding is the one a reader sees without scrolling. */
|
|
79
|
+
function bySeverity(findings) {
|
|
80
|
+
return [...findings].sort((a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity));
|
|
81
|
+
}
|
|
82
|
+
/** Markdown table cells cannot contain a raw pipe or newline. */
|
|
83
|
+
function cell(text) {
|
|
84
|
+
return String(text ?? "").replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The screen-reader layer's section.
|
|
88
|
+
*
|
|
89
|
+
* Every finding quotes the announcement it came from, because that is the entire value proposition: a
|
|
90
|
+
* rule scanner can say "this control has no accessible name", and only this can say "a screen reader
|
|
91
|
+
* announced it as `button` and a user would hear nothing else".
|
|
92
|
+
*/
|
|
93
|
+
function findingsSection(findings, limit) {
|
|
94
|
+
if (findings.length === 0) {
|
|
95
|
+
return ["**No lived-experience findings.** The screen-reader layer found nothing it could evidence."];
|
|
96
|
+
}
|
|
97
|
+
const shown = bySeverity(findings).slice(0, limit);
|
|
98
|
+
const lines = [
|
|
99
|
+
`**${findings.length} lived-experience finding(s)**`,
|
|
100
|
+
"",
|
|
101
|
+
"| | WCAG | Issue | What the screen reader announced |",
|
|
102
|
+
"|---|---|---|---|",
|
|
103
|
+
];
|
|
104
|
+
for (const f of shown) {
|
|
105
|
+
// #1366: a referral says so beside its severity, which is otherwise the most declarative word on the row.
|
|
106
|
+
const kind = isReferral(f) ? ", referred" : "";
|
|
107
|
+
lines.push(`| ${ICON[f.severity] ?? "•"} ${cell(f.severity)}${kind} | ${cell(f.wcag)} | ${cell(f.issue)} | \`${cell(f.evidence)}\` |`);
|
|
108
|
+
}
|
|
109
|
+
if (shown.some(isReferral))
|
|
110
|
+
lines.push("", REFERRED_NOTE);
|
|
111
|
+
// Never a silent cap. A truncated report that looks complete is how a real finding gets missed, and
|
|
112
|
+
// this project has the scars: a run once reported success while a probe had crashed 604 times.
|
|
113
|
+
if (findings.length > shown.length) {
|
|
114
|
+
lines.push("", `_… and ${findings.length - shown.length} more, omitted to keep this summary within GitHub's size limit. The full JSON is in the workflow artifacts._`);
|
|
115
|
+
}
|
|
116
|
+
return lines;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* #1366: what "referred" on a findings row means, in the words `outcomeSection` below already uses for the count.
|
|
120
|
+
* Stated once, and only when a shown row is a referral.
|
|
121
|
+
*/
|
|
122
|
+
const REFERRED_NOTE = "_A finding marked **referred** is worth a person's eyes: the tool cannot decide it on its own, so "
|
|
123
|
+
+ "it is not an assertion that the criterion fails, however its severity reads._";
|
|
124
|
+
/**
|
|
125
|
+
* What was NOT determined, as a count — the half a findings list cannot express.
|
|
126
|
+
*
|
|
127
|
+
* A findings table answers "what did you find". It cannot answer "what could you not tell", and on a PR
|
|
128
|
+
* comment those read the same: an empty table looks like a clean page. `criterionOutcomes` has computed
|
|
129
|
+
* the difference all along and this renderer discarded it.
|
|
130
|
+
*
|
|
131
|
+
* Silent when the result carries no outcomes: an older run cannot say, and inventing a tally of zeroes
|
|
132
|
+
* would turn "we did not record this" into "nothing was undetermined", which is the exact substitution
|
|
133
|
+
* this whole file is written against.
|
|
134
|
+
*/
|
|
135
|
+
function outcomeSection(outcomes) {
|
|
136
|
+
if (!outcomes)
|
|
137
|
+
return [];
|
|
138
|
+
const tally = (name) => outcomes.filter((o) => o.outcome === name).length;
|
|
139
|
+
const undetermined = tally("cantTell");
|
|
140
|
+
const untested = tally("untested");
|
|
141
|
+
if (undetermined === 0 && untested === 0)
|
|
142
|
+
return [];
|
|
143
|
+
const partial = partialExaminationCount(outcomes);
|
|
144
|
+
// #1563: the one reason among the referrals that is about this tool rather than the page, so it is counted apart.
|
|
145
|
+
const cutShort = partial
|
|
146
|
+
? ` **${partial} of the referred criteria rest on an examination known to be partial:** a sweep stopped short `
|
|
147
|
+
+ "of the page, so they were not examined in full."
|
|
148
|
+
: "";
|
|
149
|
+
// #254: this renders on a STRANGER'S pull request, with no legend and no chance to ask -- worse than
|
|
150
|
+
// #242's `report.ts` instance, which at least reaches someone who ran the CLI and can scroll up to one.
|
|
151
|
+
// `cantTell` is ACT's own vocabulary term (still what `--json`/`ActOutcome` emit, untouched); a reviewer
|
|
152
|
+
// who has never read the ACT spec reads it as a typo or an accusation. Uses #242's own wording, `ceo`'s
|
|
153
|
+
// ruling on PR #252 -- "referred" (worth a person's eyes, the tool cannot decide it) rather than a third
|
|
154
|
+
// spelling of the same distinction. UNLIKE report.ts, no legend exists here to house even one
|
|
155
|
+
// parenthetical ACT mention, so the term does not appear at all -- the acceptance for this row forbids
|
|
156
|
+
// it outright, correctly: a reader with no legend gets no term to misread.
|
|
157
|
+
return ["", `**Not determined:** ${undetermined} criteria we cover were referred — worth a person's `
|
|
158
|
+
+ `eyes, the tool cannot decide these on its own — and ${untested} are not covered by any assessor `
|
|
159
|
+
+ `of ours. Neither is a pass — see the run artifact for the per-criterion reasons.${cutShort}`];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* #1388: IS THIS RULE-LAYER ROW ABOUT CONTENT INSIDE A FRAME?
|
|
163
|
+
*
|
|
164
|
+
* axe's `target` holds one selector per frame boundary it crossed, so a target of more than one entry points inside an
|
|
165
|
+
* iframe. It says the node is in A frame, never WHOSE: a same-origin frame is the author's own, and the result records no
|
|
166
|
+
* frame origin, so the marker claims no more than that. A single entry that is itself an array is a shadow-DOM path, not
|
|
167
|
+
* a frame. A row with no nodes (an older result) cannot be told, so it is not marked.
|
|
168
|
+
*/
|
|
169
|
+
export function insideFrame(row) {
|
|
170
|
+
return (row.nodes ?? []).some((node) => Array.isArray(node.target) && node.target.length > 1);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* #1388: the caveat the run artifact already carries (Conformance Requirement 3's limitation: "Third-party content …
|
|
174
|
+
* is also not distinguished from the author's own"), stated beside the table it concerns, and only when a row is inside a
|
|
175
|
+
* frame. Rehearsal 3 read "Rule layer (axe-core): 3 violation(s)" on w3.org/WAI, all three inside the embedded YouTube
|
|
176
|
+
* player, with the caveat two layers down in the JSON.
|
|
177
|
+
*/
|
|
178
|
+
const FRAME_CAVEAT = "_A row marked **in a frame** concerns content inside an embedded frame. This run did not examine "
|
|
179
|
+
+ "whose frame it is, so it may be third-party content (an embed, advert or widget) the page's author cannot control._";
|
|
180
|
+
/**
|
|
181
|
+
* #1391: THE EVIDENCE A RUN COLLECTED THAT NOTHING FAILED ON. Rehearsal 3's reader: *"the one piece of evidence that
|
|
182
|
+
* would have shown me what this tool is for was collected, stored, and then not shown to me."*
|
|
183
|
+
*
|
|
184
|
+
* EVIDENCE OBSERVED, NEVER A PASS. Each line quotes what the screen reader said before and after one control was
|
|
185
|
+
* activated. It names no criterion and says nothing passed: ADR 0021 gives the RULE the right to conclude about
|
|
186
|
+
* `4.1.2:state-change-silent`, and one clean control is not a conclusion about the page. Silent when there is
|
|
187
|
+
* nothing to show, so a report without this evidence reads exactly as it did.
|
|
188
|
+
*/
|
|
189
|
+
function stateChangeSection(observed) {
|
|
190
|
+
if (!observed?.length)
|
|
191
|
+
return [];
|
|
192
|
+
// A code span ends at a backtick, so one inside an announcement is replaced rather than escaped.
|
|
193
|
+
const quote = (text) => `\`${cell(text).replace(/`/g, "'")}\``;
|
|
194
|
+
return [
|
|
195
|
+
"",
|
|
196
|
+
`**Evidence observed: ${observed.length} state change(s) the screen reader announced after activation**`,
|
|
197
|
+
"",
|
|
198
|
+
...observed.map((change) => `- ${quote(change.control)} → ${quote(change.after)} `
|
|
199
|
+
+ `(${cell(change.from)} → ${cell(change.to)})`),
|
|
200
|
+
"",
|
|
201
|
+
"<sub>What one control said before and after it was activated, not a result for the page.</sub>",
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The rule layer's section.
|
|
206
|
+
*
|
|
207
|
+
* "not run" and "0 violations" must never look alike: one means the visual criteria are unchecked, the
|
|
208
|
+
* other means they were checked and passed. Reporting silence as a clean bill of health is the single
|
|
209
|
+
* most misleading thing this tool could do — the same rule `src/report.ts` states for the CLI.
|
|
210
|
+
*/
|
|
211
|
+
function ruleSection(ruleBased, limit) {
|
|
212
|
+
if (ruleBased === null) {
|
|
213
|
+
return ["**Rule layer (axe-core): not run.** Visual criteria such as contrast are *unchecked*, not clean."];
|
|
214
|
+
}
|
|
215
|
+
if (ruleBased.length === 0)
|
|
216
|
+
return ["**Rule layer (axe-core): 0 violations.**"];
|
|
217
|
+
// #1388: the count line does not silently absorb rows about content inside a frame. A split count, not a bare total.
|
|
218
|
+
const framed = ruleBased.filter(insideFrame).length;
|
|
219
|
+
const split = framed > 0 ? `, ${framed} inside a frame` : "";
|
|
220
|
+
const lines = [`**Rule layer (axe-core): ${ruleBased.length} violation(s)${split}**`, "", "| Impact | WCAG | Rule |", "|---|---|---|"];
|
|
221
|
+
for (const v of ruleBased.slice(0, limit)) {
|
|
222
|
+
const marker = insideFrame(v) ? " _(in a frame; origin not examined)_" : "";
|
|
223
|
+
lines.push(`| ${cell(v.impact)} | ${cell((v.wcag ?? []).join(", ")) || "—"} | ${cell(v.rule)}: ${cell(v.help)}${marker} |`);
|
|
224
|
+
}
|
|
225
|
+
if (ruleBased.length > limit)
|
|
226
|
+
lines.push("", `_… and ${ruleBased.length - limit} more._`);
|
|
227
|
+
if (framed > 0)
|
|
228
|
+
lines.push("", FRAME_CAVEAT);
|
|
229
|
+
return lines;
|
|
230
|
+
}
|
|
231
|
+
const DEFAULT_LIMIT = 20;
|
|
232
|
+
/** See SummaryOptions.taskQuestion: true for the shipped local scorer, which never sees the task. No
|
|
233
|
+
* trailing colon -- `blockerCountLine` adds its own, matching `taskVerdictLabel().question`'s own text
|
|
234
|
+
* (`@a11ign/judge`) exactly, so this default is not a second, independently-punctuated copy of it. */
|
|
235
|
+
const DEFAULT_TASK_QUESTION = "No blocking findings";
|
|
236
|
+
/**
|
|
237
|
+
* The line `taskQuestion` renders as, when `taskCompletable` is not really an answer to a question --
|
|
238
|
+
* the SAME split `report.ts`'s `verdictHeadline` makes, so the two consumers cannot answer the same
|
|
239
|
+
* verdict two different ways. A count, never a bare yes/no: a number cannot contradict a list of the
|
|
240
|
+
* same things the way "No" can when the list right below it is all findings.
|
|
241
|
+
*/
|
|
242
|
+
function blockerCountLine(label, findings) {
|
|
243
|
+
const blockers = findings.filter((f) => f.severity === "blocker").length;
|
|
244
|
+
const rest = findings.length - blockers;
|
|
245
|
+
const others = rest ? `; ${rest} finding(s) below that severity` : "";
|
|
246
|
+
return `**${label}:** ${blockers === 0 ? "none" : blockers}${others}`;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* The Action's log lines -- EXPORTED so a test drives the real wording (#1363).
|
|
250
|
+
*
|
|
251
|
+
* When the examination ended because an activation left the site, that is said FIRST. Rehearsal 2's log read
|
|
252
|
+
* `a11ign: 1 finding(s) (1 serious)` about a finding observed on youtube.com, and a reader of that line alone
|
|
253
|
+
* would have filed a 2.4.2 bug against the W3C.
|
|
254
|
+
*/
|
|
255
|
+
export function logLines(result, failOn) {
|
|
256
|
+
const { findings } = result.verdict;
|
|
257
|
+
const breakdown = findingBreakdown(findings);
|
|
258
|
+
const left = result.leftSite;
|
|
259
|
+
const lines = [];
|
|
260
|
+
if (left) {
|
|
261
|
+
lines.push(`a11ign: examination ENDED -- left the site at ${JSON.stringify(left.control)}`
|
|
262
|
+
+ `${left.to ? ` (to ${left.to})` : ""}; everything after it was NOT EXAMINED`);
|
|
263
|
+
}
|
|
264
|
+
// #1387: rehearsal 5's reader found nothing about it "in the log, the count, or the three documents".
|
|
265
|
+
if (documentsSpannedSentence(result.conformance)) {
|
|
266
|
+
lines.push("a11ign: this capture named MORE THAN ONE DOCUMENT -- its evidence was gathered across more "
|
|
267
|
+
+ "than one page (see the summary)");
|
|
268
|
+
}
|
|
269
|
+
const partial = partialExaminationCount(result.outcomes);
|
|
270
|
+
if (partial) {
|
|
271
|
+
lines.push(`a11ign: ${partial} criteria rest on an examination known to be partial -- see the artifact`);
|
|
272
|
+
}
|
|
273
|
+
lines.push(`a11ign: ${findings.length} finding(s) (${breakdown})${left ? " in what was examined" : ""}; `
|
|
274
|
+
+ `fail-on=${failOn}`);
|
|
275
|
+
// #1618: its own line, so the count line above stays as #1366 pinned it, and only when a threshold is set -- at `never`
|
|
276
|
+
// nothing fails the run, and the rule would be a sentence about an exit that cannot happen.
|
|
277
|
+
if (failOn !== "never")
|
|
278
|
+
lines.push(`a11ign: ${FAIL_ON_RULE}`);
|
|
279
|
+
return lines;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* #1366: THE LOG LINE'S BREAKDOWN, ASSERTED AND REFERRED APART. A severity belongs to an assertion; a referral is
|
|
283
|
+
* counted as a referral and never as its severity, because "1 serious" read as a failure to rehearsal 2's reader
|
|
284
|
+
* about a finding the judge had only referred. Examples: "1 referred"; "2 asserted: 1 serious, 1 moderate; 1 referred".
|
|
285
|
+
*/
|
|
286
|
+
function findingBreakdown(findings) {
|
|
287
|
+
const asserted = findings.filter((f) => !isReferral(f));
|
|
288
|
+
const referred = findings.length - asserted.length;
|
|
289
|
+
const counts = asserted.reduce((acc, f) => {
|
|
290
|
+
acc[f.severity] = (acc[f.severity] ?? 0) + 1;
|
|
291
|
+
return acc;
|
|
292
|
+
}, {});
|
|
293
|
+
const severities = Object.entries(counts).map(([s, n]) => `${n} ${s}`).join(", ");
|
|
294
|
+
const parts = [
|
|
295
|
+
...(asserted.length ? [`${asserted.length} asserted: ${severities}`] : []),
|
|
296
|
+
...(referred ? [`${referred} referred`] : []),
|
|
297
|
+
];
|
|
298
|
+
return parts.join("; ") || "none";
|
|
299
|
+
}
|
|
300
|
+
/** The summary's warning when the examination ended early (#1363), above everything it did find. */
|
|
301
|
+
function leftSiteLead(left) {
|
|
302
|
+
return [
|
|
303
|
+
`> **The examination ended early.** Activating ${JSON.stringify(left.control)} took the browser off this `
|
|
304
|
+
+ `site${left.to ? ` (to ${left.to})` : ""}. Nothing observed after that point is reported as this page's, `
|
|
305
|
+
+ "and the sweeps and probes that would have run after it were not examined.",
|
|
306
|
+
"",
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
export function renderSummary(result, options = {}) {
|
|
310
|
+
const taskQuestion = options.taskQuestion ?? DEFAULT_TASK_QUESTION;
|
|
311
|
+
const isTaskClaim = options.isTaskClaim ?? false;
|
|
312
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
313
|
+
const { verdict } = result;
|
|
314
|
+
const lines = [];
|
|
315
|
+
if (options.marker)
|
|
316
|
+
lines.push(`<!-- ${options.marker} -->`);
|
|
317
|
+
// Lead with it, and show nothing else. A reader who skims must not come away with a finding that was
|
|
318
|
+
// never about their page.
|
|
319
|
+
if (result.captureVerified === false) {
|
|
320
|
+
lines.push("## a11ign — **could not read this page**", "", `**Page:** ${result.url}`, "", result.captureUnverifiedReason === "contained"
|
|
321
|
+
? "The screen reader reached almost none of this page. The page itself exposes many headings and "
|
|
322
|
+
+ "landmarks, and quick navigation could not get to them — the signature of a modal dialog holding "
|
|
323
|
+
+ "the screen reader in place, most often a cookie or consent banner. A keyboard user meets the "
|
|
324
|
+
+ "same wall, but what we captured describes the dialog, not your content."
|
|
325
|
+
: "The capture could not be confirmed to have read the requested page: what the screen reader "
|
|
326
|
+
+ "announced did not contain the page's own title, after three attempts. That usually means it read "
|
|
327
|
+
+ "browser chrome — an interstitial, a consent dialog, an image or PDF viewer — rather than your "
|
|
328
|
+
+ "content.", "", result.captureUnverifiedReason === "contained"
|
|
329
|
+
? "**No findings are reported.** Any that were produced would describe the consent dialog rather "
|
|
330
|
+
+ "than your page, and a report about somebody else's cookie banner is worse than saying nothing."
|
|
331
|
+
: "**No findings are reported.** Any that were produced would describe the browser, not this page, "
|
|
332
|
+
+ "and blaming a site for its browser is worse than saying nothing.", "", "<sub>The full capture is in the run artifact if you want to see what it did read.</sub>");
|
|
333
|
+
return lines.join("\n");
|
|
334
|
+
}
|
|
335
|
+
if (result.leftSite)
|
|
336
|
+
lines.push(...leftSiteLead(result.leftSite));
|
|
337
|
+
const spanned = documentsSpannedSentence(result.conformance);
|
|
338
|
+
// #1387: said above the heading, like the early end above, because every finding below may describe either page.
|
|
339
|
+
if (spanned) {
|
|
340
|
+
lines.push(`> **This capture's evidence spans more than one document.** ${spanned} So what follows is not `
|
|
341
|
+
+ "only about the page requested.", "");
|
|
342
|
+
}
|
|
343
|
+
lines.push("## a11ign — what a screen reader actually experienced", "", `**Page:** ${result.url}`, `**Task:** ${result.task}`, `**Screen reader:** ${result.screenReader}${result.transcript ? ` · ${result.transcript.length} announcements` : ""}`, "",
|
|
344
|
+
// See SummaryOptions.taskQuestion/isTaskClaim. This is posted on a PULL REQUEST in bold, and with
|
|
345
|
+
// the shipped local scorer it used to ask "could a screen-reader user complete the task?" (or claim
|
|
346
|
+
// "No blocking findings") and answer from a signal that never saw the task -- a report of six SERIOUS
|
|
347
|
+
// findings once read "**No blocking findings** Yes" above the very table listing them.
|
|
348
|
+
isTaskClaim
|
|
349
|
+
? `**${taskQuestion}** ${verdict.taskCompletable ? "Yes" : "**No**"}`
|
|
350
|
+
: blockerCountLine(taskQuestion, verdict.findings), "", verdict.summary, "", ...findingsSection(verdict.findings, limit), ...outcomeSection(result.outcomes), ...stateChangeSection(options.stateChangesObserved), "", ...ruleSection(result.ruleBased, limit), "", "<sub>Two layers, deliberately. The screen-reader layer judges the lived experience; axe-core covers "
|
|
351
|
+
+ "the visual and rule-based criteria a screen reader cannot perceive. Neither replaces the other, "
|
|
352
|
+
+ "and neither replaces a human.</sub>");
|
|
353
|
+
return lines.join("\n");
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/action/summary.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAsBH,+HAA+H;AAC/H,SAAS,UAAU,CAAC,OAAmB;IACrC,OAAO,OAAO,CAAC,OAAO,KAAK,aAAa,CAAC;AAC3C,CAAC;AAgED;;;;;;;;GAQG;AACH,MAAM,iBAAiB,GAAG,kEAAkE,CAAC;AAE7F,MAAM,UAAU,wBAAwB,CAAC,WAAqC;IAC5E,KAAK,MAAM,WAAW,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/D,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG,IAAI,MAAM,CAAC,kEAAkE;MACrG,2GAA2G,CAAC,CAAC;AAEjH,+GAA+G;AAC/G,MAAM,UAAU,uBAAuB,CAAC,QAA+B;IACrE,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/G,CAAC;AAED,2EAA2E;AAC3E,MAAM,cAAc,GAAe,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAI/E,8IAA8I;AAC9I,MAAM,YAAY,GAAG,+EAA+E,CAAC;AAErG;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,QAAsB,EAAE,MAAc;IAC/D,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACrC,yHAAyH;IACzH,iGAAiG;IACjG,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,+FAA+F;IAC/F,2FAA2F;IAC3F,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzF,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAChD,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,IAAI,SAAS,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,IAAI,GAA6B;IACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI;CAC1D,CAAC;AAEF,iGAAiG;AACjG,SAAS,UAAU,CAAC,QAAsB;IACxC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CACvB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAClF,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,SAAS,IAAI,CAAC,IAAY;IACxB,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAChF,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,QAAsB,EAAE,KAAa;IAC5D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,4FAA4F,CAAC,CAAC;IACxG,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG;QACZ,KAAK,QAAQ,CAAC,MAAM,gCAAgC;QACpD,EAAE;QACF,uDAAuD;QACvD,mBAAmB;KACpB,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,0GAA0G;QAC1G,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzI,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC1D,oGAAoG;IACpG,+FAA+F;IAC/F,IAAI,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,8GAA8G,CAAC,CAAC;IACzK,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,aAAa,GAAG,oGAAoG;MACtH,+EAA+E,CAAC;AAEpF;;;;;;;;;;GAUG;AACH,SAAS,cAAc,CAAC,QAA+B;IACrD,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IAClF,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,YAAY,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpD,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClD,kHAAkH;IAClH,MAAM,QAAQ,GAAG,OAAO;QACtB,CAAC,CAAC,MAAM,OAAO,gGAAgG;cAC3G,iDAAiD;QACrD,CAAC,CAAC,EAAE,CAAC;IACP,qGAAqG;IACrG,wGAAwG;IACxG,yGAAyG;IACzG,wGAAwG;IACxG,yGAAyG;IACzG,8FAA8F;IAC9F,uGAAuG;IACvG,2EAA2E;IAC3E,OAAO,CAAC,EAAE,EAAE,uBAAuB,YAAY,sDAAsD;cACjG,uDAAuD,QAAQ,mCAAmC;cAClG,mFAAmF,QAAQ,EAAE,CAAC,CAAC;AACrG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,GAAgD;IAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChG,CAAC;AAED;;;;;GAKG;AACH,MAAM,YAAY,GAAG,mGAAmG;MACpH,qHAAqH,CAAC;AAE1H;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CAAC,QAAoD;IAC9E,IAAI,CAAC,QAAQ,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IACjC,iGAAiG;IACjG,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;IACvE,OAAO;QACL,EAAE;QACF,wBAAwB,QAAQ,CAAC,MAAM,iEAAiE;QACxG,EAAE;QACF,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG;cAC9E,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC;QAClD,EAAE;QACF,gGAAgG;KACjG,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,SAAiC,EAAE,KAAa;IACnE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,kGAAkG,CAAC,CAAC;IAC9G,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,0CAA0C,CAAC,CAAC;IAChF,qHAAqH;IACrH,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAG,CAAC,4BAA4B,SAAS,CAAC,MAAM,gBAAgB,KAAK,IAAI,EAAE,EAAE,EAAE,0BAA0B,EAAE,eAAe,CAAC,CAAC;IACvI,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC;IAC9H,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,SAAS,CAAC,MAAM,GAAG,KAAK,SAAS,CAAC,CAAC;IAC1F,IAAI,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC;AACf,CAAC;AA8CD,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB;;uGAEuG;AACvG,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAErD;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,KAAa,EAAE,QAA+B;IACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,OAAO,KAAK,KAAK,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,GAAG,MAAM,EAAE,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAiB,EAAE,MAAc;IACxD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;IACpC,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,iDAAiD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;cACtF,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,wCAAwC,CAAC,CAAC;IACpF,CAAC;IACD,sGAAsG;IACtG,IAAI,wBAAwB,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,6FAA6F;cACpG,iCAAiC,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,0EAA0E,CAAC,CAAC;IAC3G,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,MAAM,gBAAgB,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,IAAI;UACrG,WAAW,MAAM,EAAE,CAAC,CAAC;IACzB,wHAAwH;IACxH,4FAA4F;IAC5F,IAAI,MAAM,KAAK,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,YAAY,EAAE,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,QAA+B;IACvD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAyB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QAChE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,cAAc,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;AACpC,CAAC;AAED,oGAAoG;AACpG,SAAS,YAAY,CAAC,IAAwC;IAC5D,OAAO;QACL,iDAAiD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,6BAA6B;cACtG,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,kEAAkE;cAC1G,2EAA2E;QAC/E,EAAE;KACH,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,UAA0B,EAAE;IAC3E,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,qBAAqB,CAAC;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;IAC7C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAC3B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC;IAC7D,qGAAqG;IACrG,0BAA0B;IAC1B,IAAI,MAAM,CAAC,eAAe,KAAK,KAAK,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CACR,0CAA0C,EAC1C,EAAE,EACF,aAAa,MAAM,CAAC,GAAG,EAAE,EACzB,EAAE,EACF,MAAM,CAAC,uBAAuB,KAAK,WAAW;YAC5C,CAAC,CAAC,gGAAgG;kBAC9F,kGAAkG;kBAClG,+FAA+F;kBAC/F,yEAAyE;YAC7E,CAAC,CAAC,6FAA6F;kBAC3F,mGAAmG;kBACnG,gGAAgG;kBAChG,UAAU,EAChB,EAAE,EACF,MAAM,CAAC,uBAAuB,KAAK,WAAW;YAC5C,CAAC,CAAC,gGAAgG;kBAC9F,gGAAgG;YACpG,CAAC,CAAC,kGAAkG;kBAChG,kEAAkE,EACxE,EAAE,EACF,yFAAyF,CAC1F,CAAC;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,wBAAwB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC7D,iHAAiH;IACjH,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,+DAA+D,OAAO,0BAA0B;cACvG,gCAAgC,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,IAAI,CACR,uDAAuD,EACvD,EAAE,EACF,aAAa,MAAM,CAAC,GAAG,EAAE,EACzB,aAAa,MAAM,CAAC,IAAI,EAAE,EAC1B,sBAAsB,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,EACrH,EAAE;IACF,kGAAkG;IAClG,oGAAoG;IACpG,sGAAsG;IACtG,uFAAuF;IACvF,WAAW;QACT,CAAC,CAAC,KAAK,YAAY,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE;QACrE,CAAC,CAAC,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,EACpD,EAAE,EACF,OAAO,CAAC,OAAO,EACf,EAAE,EACF,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC3C,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,EAClC,GAAG,kBAAkB,CAAC,OAAO,CAAC,oBAAoB,CAAC,EACnD,EAAE,EACF,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,EACvC,EAAE,EACF,sGAAsG;UAClG,kGAAkG;UAClG,qCAAqC,CAC1C,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|