@a11yst/reporters 1.0.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 +373 -0
- package/README.md +78 -0
- package/dist/assets.d.ts +3 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +271 -0
- package/dist/assets.js.map +1 -0
- package/dist/baseline-report.d.ts +25 -0
- package/dist/baseline-report.d.ts.map +1 -0
- package/dist/baseline-report.js +426 -0
- package/dist/baseline-report.js.map +1 -0
- package/dist/finding-groups.d.ts +14 -0
- package/dist/finding-groups.d.ts.map +1 -0
- package/dist/finding-groups.js +77 -0
- package/dist/finding-groups.js.map +1 -0
- package/dist/finding-source-report.d.ts +21 -0
- package/dist/finding-source-report.d.ts.map +1 -0
- package/dist/finding-source-report.js +134 -0
- package/dist/finding-source-report.js.map +1 -0
- package/dist/github/escape.d.ts +13 -0
- package/dist/github/escape.d.ts.map +1 -0
- package/dist/github/escape.js +37 -0
- package/dist/github/escape.js.map +1 -0
- package/dist/github/generate.d.ts +3 -0
- package/dist/github/generate.d.ts.map +1 -0
- package/dist/github/generate.js +140 -0
- package/dist/github/generate.js.map +1 -0
- package/dist/github/source-location.d.ts +4 -0
- package/dist/github/source-location.d.ts.map +1 -0
- package/dist/github/source-location.js +69 -0
- package/dist/github/source-location.js.map +1 -0
- package/dist/github/types.d.ts +42 -0
- package/dist/github/types.d.ts.map +1 -0
- package/dist/github/types.js +18 -0
- package/dist/github/types.js.map +1 -0
- package/dist/html.d.ts +8 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +562 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/escape.d.ts +5 -0
- package/dist/markdown/escape.d.ts.map +1 -0
- package/dist/markdown/escape.js +27 -0
- package/dist/markdown/escape.js.map +1 -0
- package/dist/markdown/generate.d.ts +3 -0
- package/dist/markdown/generate.d.ts.map +1 -0
- package/dist/markdown/generate.js +498 -0
- package/dist/markdown/generate.js.map +1 -0
- package/dist/markdown/links.d.ts +4 -0
- package/dist/markdown/links.d.ts.map +1 -0
- package/dist/markdown/links.js +30 -0
- package/dist/markdown/links.js.map +1 -0
- package/dist/markdown/types.d.ts +71 -0
- package/dist/markdown/types.d.ts.map +1 -0
- package/dist/markdown/types.js +2 -0
- package/dist/markdown/types.js.map +1 -0
- package/dist/profile-evidence.d.ts +21 -0
- package/dist/profile-evidence.d.ts.map +1 -0
- package/dist/profile-evidence.js +211 -0
- package/dist/profile-evidence.js.map +1 -0
- package/dist/validation.d.ts +10 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +96 -0
- package/dist/validation.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { encodeMarkdownLinkTarget, escapeMarkdownLinkLabel, } from "./escape.js";
|
|
2
|
+
const UNIX_ABSOLUTE = /^\//;
|
|
3
|
+
const WINDOWS_ABSOLUTE = /^[A-Za-z]:[\\/]/;
|
|
4
|
+
const FILE_URI = /^file:/i;
|
|
5
|
+
export function isSafeRelativeLinkPath(path) {
|
|
6
|
+
const trimmed = path.trim().replace(/\\/g, "/");
|
|
7
|
+
if (!trimmed ||
|
|
8
|
+
UNIX_ABSOLUTE.test(trimmed) ||
|
|
9
|
+
WINDOWS_ABSOLUTE.test(trimmed) ||
|
|
10
|
+
FILE_URI.test(trimmed)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const segments = trimmed.split("/");
|
|
14
|
+
return !segments.some((segment) => segment === ".." || segment === "");
|
|
15
|
+
}
|
|
16
|
+
export function buildSafeMarkdownLink(label, path, diagnostics) {
|
|
17
|
+
if (!path || !isSafeRelativeLinkPath(path)) {
|
|
18
|
+
if (path) {
|
|
19
|
+
diagnostics.push({
|
|
20
|
+
code: "invalid-link",
|
|
21
|
+
level: "warning",
|
|
22
|
+
message: `Omitted unsafe artifact link: ${label}.`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return escapeMarkdownLinkLabel(label);
|
|
26
|
+
}
|
|
27
|
+
const encoded = encodeMarkdownLinkTarget(path);
|
|
28
|
+
return `[${escapeMarkdownLinkLabel(label)}](${encoded})`;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/markdown/links.ts"],"names":[],"mappings":"AACA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAC3C,MAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChD,IACE,CAAC,OAAO;QACR,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC;QAC3B,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,KAAa,EACb,IAAwB,EACxB,WAAuC;IAEvC,IAAI,CAAC,IAAI,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,IAAI,IAAI,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,iCAAiC,KAAK,GAAG;aACnD,CAAC,CAAC;QACL,CAAC;QACD,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,OAAO,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC/C,OAAO,IAAI,uBAAuB,CAAC,KAAK,CAAC,KAAK,OAAO,GAAG,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AuditProfileSummary, BaselineSummary, ComparisonCoverage, Finding, PolicyEvaluationResult, ResolvedFinding, Severity } from "@a11yst/types";
|
|
2
|
+
export type ReportReference = {
|
|
3
|
+
path: string;
|
|
4
|
+
};
|
|
5
|
+
export type MarkdownAuditMetadata = {
|
|
6
|
+
auditId?: string;
|
|
7
|
+
project?: string;
|
|
8
|
+
framework?: string;
|
|
9
|
+
target?: string;
|
|
10
|
+
startedAt?: string;
|
|
11
|
+
routes?: string[];
|
|
12
|
+
profiles?: string[];
|
|
13
|
+
viewports?: string[];
|
|
14
|
+
uniqueIssueGroups?: number;
|
|
15
|
+
totalAffectedElements?: number;
|
|
16
|
+
findingsBySeverity?: Partial<Record<Severity, number>>;
|
|
17
|
+
profileSummary?: AuditProfileSummary;
|
|
18
|
+
executionFailed?: boolean;
|
|
19
|
+
failureMessages?: string[];
|
|
20
|
+
};
|
|
21
|
+
export type MarkdownReportInput = {
|
|
22
|
+
product: {
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
};
|
|
26
|
+
audit: {
|
|
27
|
+
successful: boolean;
|
|
28
|
+
};
|
|
29
|
+
metadata?: MarkdownAuditMetadata;
|
|
30
|
+
findings: Finding[];
|
|
31
|
+
resolvedFindings?: ResolvedFinding[];
|
|
32
|
+
baselineSummary?: BaselineSummary;
|
|
33
|
+
comparisonCoverage?: ComparisonCoverage;
|
|
34
|
+
policyEvaluation?: PolicyEvaluationResult;
|
|
35
|
+
policyMinimumSeverity?: Severity;
|
|
36
|
+
reports?: {
|
|
37
|
+
html?: ReportReference;
|
|
38
|
+
sarif?: ReportReference;
|
|
39
|
+
junit?: ReportReference;
|
|
40
|
+
markdown?: ReportReference;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export type MarkdownReportOptions = {
|
|
44
|
+
title?: string;
|
|
45
|
+
includeKnownFindings?: boolean;
|
|
46
|
+
includeClassifications?: boolean;
|
|
47
|
+
includeResolvedSummary?: boolean;
|
|
48
|
+
maxDetailedFindings?: number;
|
|
49
|
+
};
|
|
50
|
+
export type MarkdownReportDiagnosticCode = "truncated-findings" | "invalid-link" | "missing-policy-data" | "unsupported-lifecycle" | "redacted-content";
|
|
51
|
+
export type MarkdownReportDiagnostic = {
|
|
52
|
+
code: MarkdownReportDiagnosticCode;
|
|
53
|
+
level: "info" | "warning" | "error";
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
export type MarkdownReportResult = {
|
|
57
|
+
markdown: string;
|
|
58
|
+
summary: {
|
|
59
|
+
findings: number;
|
|
60
|
+
newFindings: number;
|
|
61
|
+
knownFindings: number;
|
|
62
|
+
regressedFindings: number;
|
|
63
|
+
resolvedFindings: number;
|
|
64
|
+
notComparedFindings: number;
|
|
65
|
+
policyBreaches: number;
|
|
66
|
+
detailedFindings: number;
|
|
67
|
+
truncatedFindings: number;
|
|
68
|
+
};
|
|
69
|
+
diagnostics: MarkdownReportDiagnostic[];
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/markdown/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/markdown/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AuditRunResult, FocusStep, ProfileSnapshot } from "@a11yst/types";
|
|
2
|
+
export interface FocusSequenceEvidence {
|
|
3
|
+
forwardSteps: FocusStep[];
|
|
4
|
+
backwardSteps: FocusStep[];
|
|
5
|
+
stopReason?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LayoutComparisonEvidence {
|
|
8
|
+
baseline?: ProfileSnapshot;
|
|
9
|
+
scaled?: ProfileSnapshot;
|
|
10
|
+
}
|
|
11
|
+
export interface LoadedProfileEvidence {
|
|
12
|
+
focusSequence?: FocusSequenceEvidence;
|
|
13
|
+
layoutComparison?: LayoutComparisonEvidence;
|
|
14
|
+
beforeScreenshot?: string;
|
|
15
|
+
afterScreenshot?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function loadProfileEvidenceForReport(outputDirectory: string, runs: AuditRunResult[]): Promise<Map<string, LoadedProfileEvidence>>;
|
|
18
|
+
export declare function renderFocusSequenceBlock(run: AuditRunResult, evidence: FocusSequenceEvidence): string;
|
|
19
|
+
export declare function renderLargeTextComparisonBlock(run: AuditRunResult, evidence: LoadedProfileEvidence): string;
|
|
20
|
+
export declare function renderProfileEvidenceSection(runs: AuditRunResult[], loaded: Map<string, LoadedProfileEvidence>): string;
|
|
21
|
+
//# sourceMappingURL=profile-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-evidence.d.ts","sourceRoot":"","sources":["../src/profile-evidence.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAuCD,wBAAsB,4BAA4B,CAChD,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,cAAc,EAAE,GACrB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CA0C7C;AAkCD,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,CAoDR;AAgCD,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,CAqCR;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,cAAc,EAAE,EACtB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,GACzC,MAAM,CAoBR"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function escapeHtml(value) {
|
|
4
|
+
return String(value)
|
|
5
|
+
.replaceAll("&", "&")
|
|
6
|
+
.replaceAll("<", "<")
|
|
7
|
+
.replaceAll(">", ">")
|
|
8
|
+
.replaceAll('"', """)
|
|
9
|
+
.replaceAll("'", "'");
|
|
10
|
+
}
|
|
11
|
+
function evidencePath(value) {
|
|
12
|
+
const normalized = value.replaceAll("\\", "/").replace(/^(\.\/|\/)+/, "");
|
|
13
|
+
const path = normalized
|
|
14
|
+
.split("/")
|
|
15
|
+
.filter((segment) => segment !== "" && segment !== "." && segment !== "..")
|
|
16
|
+
.map((segment) => encodeURIComponent(segment))
|
|
17
|
+
.join("/");
|
|
18
|
+
return `../${path || "evidence"}`;
|
|
19
|
+
}
|
|
20
|
+
function runRouteLabel(run) {
|
|
21
|
+
return run.route ?? run.routeName ?? run.routeId ?? "route";
|
|
22
|
+
}
|
|
23
|
+
function findDefaultScreenshot(run, runs) {
|
|
24
|
+
return runs.find((candidate) => candidate.profile === "default" &&
|
|
25
|
+
candidate.projectName === run.projectName &&
|
|
26
|
+
candidate.routeId === run.routeId &&
|
|
27
|
+
candidate.viewport?.name === run.viewport?.name &&
|
|
28
|
+
candidate.status === "completed")?.evidence?.screenshot;
|
|
29
|
+
}
|
|
30
|
+
export async function loadProfileEvidenceForReport(outputDirectory, runs) {
|
|
31
|
+
const loaded = new Map();
|
|
32
|
+
for (const run of runs) {
|
|
33
|
+
if (run.status !== "completed")
|
|
34
|
+
continue;
|
|
35
|
+
const bundle = {};
|
|
36
|
+
for (const ref of run.profileEvidence ?? []) {
|
|
37
|
+
if (!ref.path)
|
|
38
|
+
continue;
|
|
39
|
+
try {
|
|
40
|
+
const raw = await readFile(join(outputDirectory, ref.path), "utf8");
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
if (ref.kind === "focus-sequence") {
|
|
43
|
+
bundle.focusSequence = parsed;
|
|
44
|
+
}
|
|
45
|
+
if (ref.kind === "layout-comparison") {
|
|
46
|
+
bundle.layoutComparison = parsed;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Structured evidence is optional for report rendering.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (run.profile === "large-text") {
|
|
54
|
+
bundle.afterScreenshot = run.evidence?.screenshot;
|
|
55
|
+
bundle.beforeScreenshot =
|
|
56
|
+
bundle.layoutComparison?.baseline?.screenshot ??
|
|
57
|
+
findDefaultScreenshot(run, runs);
|
|
58
|
+
}
|
|
59
|
+
if (bundle.focusSequence ||
|
|
60
|
+
bundle.layoutComparison ||
|
|
61
|
+
bundle.beforeScreenshot ||
|
|
62
|
+
bundle.afterScreenshot) {
|
|
63
|
+
loaded.set(run.runId, bundle);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return loaded;
|
|
67
|
+
}
|
|
68
|
+
function formatTarget(target) {
|
|
69
|
+
if (!target || target.length === 0)
|
|
70
|
+
return "(no active element)";
|
|
71
|
+
return target.join(" ");
|
|
72
|
+
}
|
|
73
|
+
function formatBoolean(value) {
|
|
74
|
+
return value ? "yes" : "no";
|
|
75
|
+
}
|
|
76
|
+
function renderFocusStepRows(steps, direction) {
|
|
77
|
+
const filtered = steps.filter((step) => step.direction === direction);
|
|
78
|
+
if (filtered.length === 0) {
|
|
79
|
+
return `<tr><td colspan="7" class="muted">No ${direction} steps recorded.</td></tr>`;
|
|
80
|
+
}
|
|
81
|
+
return filtered
|
|
82
|
+
.map((step) => `<tr>
|
|
83
|
+
<td>${escapeHtml(String(step.index))}</td>
|
|
84
|
+
<td><code>${escapeHtml(formatTarget(step.target))}</code></td>
|
|
85
|
+
<td>${escapeHtml(step.role ?? "—")}</td>
|
|
86
|
+
<td>${escapeHtml(step.accessibleName ?? "—")}</td>
|
|
87
|
+
<td>${escapeHtml(formatBoolean(step.visible))}</td>
|
|
88
|
+
<td>${escapeHtml(formatBoolean(step.inViewport))}</td>
|
|
89
|
+
<td>${escapeHtml(step.tabindex === undefined ? "—" : String(step.tabindex))}</td>
|
|
90
|
+
</tr>`)
|
|
91
|
+
.join("");
|
|
92
|
+
}
|
|
93
|
+
export function renderFocusSequenceBlock(run, evidence) {
|
|
94
|
+
const route = runRouteLabel(run);
|
|
95
|
+
const forwardCount = evidence.forwardSteps.length;
|
|
96
|
+
const backwardCount = evidence.backwardSteps.length;
|
|
97
|
+
return `<section class="profile-evidence profile-evidence--keyboard" aria-labelledby="focus-${escapeHtml(run.runId)}">
|
|
98
|
+
<h4 id="focus-${escapeHtml(run.runId)}">Keyboard focus sequence</h4>
|
|
99
|
+
<p class="muted">${escapeHtml(run.projectName)} · ${escapeHtml(route)} · ${escapeHtml(run.viewport?.name ?? "default")}</p>
|
|
100
|
+
<dl class="metadata metadata--inline">
|
|
101
|
+
<div><dt>Forward steps</dt><dd>${forwardCount}</dd></div>
|
|
102
|
+
<div><dt>Backward sample</dt><dd>${backwardCount}</dd></div>
|
|
103
|
+
<div><dt>Stop reason</dt><dd>${escapeHtml(evidence.stopReason ?? "completed")}</dd></div>
|
|
104
|
+
</dl>
|
|
105
|
+
<h5>Forward Tab sequence</h5>
|
|
106
|
+
<div class="table-wrap" tabindex="0">
|
|
107
|
+
<table class="focus-sequence-table">
|
|
108
|
+
<caption class="visually-hidden">Forward keyboard focus steps for ${escapeHtml(route)}</caption>
|
|
109
|
+
<thead>
|
|
110
|
+
<tr>
|
|
111
|
+
<th scope="col">Step</th>
|
|
112
|
+
<th scope="col">Target</th>
|
|
113
|
+
<th scope="col">Role</th>
|
|
114
|
+
<th scope="col">Accessible name</th>
|
|
115
|
+
<th scope="col">Visible</th>
|
|
116
|
+
<th scope="col">In viewport</th>
|
|
117
|
+
<th scope="col">tabindex</th>
|
|
118
|
+
</tr>
|
|
119
|
+
</thead>
|
|
120
|
+
<tbody>${renderFocusStepRows(evidence.forwardSteps, "forward")}</tbody>
|
|
121
|
+
</table>
|
|
122
|
+
</div>
|
|
123
|
+
<h5>Backward Shift+Tab sample</h5>
|
|
124
|
+
<div class="table-wrap" tabindex="0">
|
|
125
|
+
<table class="focus-sequence-table">
|
|
126
|
+
<caption class="visually-hidden">Backward keyboard focus sample for ${escapeHtml(route)}</caption>
|
|
127
|
+
<thead>
|
|
128
|
+
<tr>
|
|
129
|
+
<th scope="col">Step</th>
|
|
130
|
+
<th scope="col">Target</th>
|
|
131
|
+
<th scope="col">Role</th>
|
|
132
|
+
<th scope="col">Accessible name</th>
|
|
133
|
+
<th scope="col">Visible</th>
|
|
134
|
+
<th scope="col">In viewport</th>
|
|
135
|
+
<th scope="col">tabindex</th>
|
|
136
|
+
</tr>
|
|
137
|
+
</thead>
|
|
138
|
+
<tbody>${renderFocusStepRows(evidence.backwardSteps, "backward")}</tbody>
|
|
139
|
+
</table>
|
|
140
|
+
</div>
|
|
141
|
+
</section>`;
|
|
142
|
+
}
|
|
143
|
+
function renderScreenshotFigure(path, label, alt) {
|
|
144
|
+
if (!path) {
|
|
145
|
+
return `<figure class="comparison-shot comparison-shot--missing">
|
|
146
|
+
<figcaption>${escapeHtml(label)}</figcaption>
|
|
147
|
+
<p class="muted">Screenshot not available.</p>
|
|
148
|
+
</figure>`;
|
|
149
|
+
}
|
|
150
|
+
return `<figure class="comparison-shot">
|
|
151
|
+
<figcaption>${escapeHtml(label)}</figcaption>
|
|
152
|
+
<img class="evidence" src="${escapeHtml(evidencePath(path))}" alt="${escapeHtml(alt)}">
|
|
153
|
+
</figure>`;
|
|
154
|
+
}
|
|
155
|
+
function formatDimensions(snapshot) {
|
|
156
|
+
if (!snapshot)
|
|
157
|
+
return "Not available";
|
|
158
|
+
const parts = [
|
|
159
|
+
snapshot.clientWidth !== undefined && snapshot.clientHeight !== undefined
|
|
160
|
+
? `viewport ${snapshot.clientWidth}×${snapshot.clientHeight}px`
|
|
161
|
+
: undefined,
|
|
162
|
+
snapshot.scrollWidth !== undefined && snapshot.scrollHeight !== undefined
|
|
163
|
+
? `scroll ${snapshot.scrollWidth}×${snapshot.scrollHeight}px`
|
|
164
|
+
: undefined,
|
|
165
|
+
].filter(Boolean);
|
|
166
|
+
return parts.length > 0 ? parts.join(", ") : "Not available";
|
|
167
|
+
}
|
|
168
|
+
export function renderLargeTextComparisonBlock(run, evidence) {
|
|
169
|
+
const route = runRouteLabel(run);
|
|
170
|
+
const scale = typeof run.profileMetadata?.scale === "number"
|
|
171
|
+
? `${run.profileMetadata.scale * 100}%`
|
|
172
|
+
: "200%";
|
|
173
|
+
const strategy = typeof run.profileMetadata?.strategy === "string"
|
|
174
|
+
? run.profileMetadata.strategy
|
|
175
|
+
: "injected-text-scale";
|
|
176
|
+
const baseline = evidence.layoutComparison?.baseline;
|
|
177
|
+
const scaled = evidence.layoutComparison?.scaled;
|
|
178
|
+
return `<section class="profile-evidence profile-evidence--large-text" aria-labelledby="layout-${escapeHtml(run.runId)}">
|
|
179
|
+
<h4 id="layout-${escapeHtml(run.runId)}">Large-text layout comparison</h4>
|
|
180
|
+
<p class="muted">${escapeHtml(run.projectName)} · ${escapeHtml(route)} · ${escapeHtml(run.viewport?.name ?? "default")}</p>
|
|
181
|
+
<dl class="metadata metadata--inline">
|
|
182
|
+
<div><dt>Strategy</dt><dd>${escapeHtml(strategy)}</dd></div>
|
|
183
|
+
<div><dt>Text scale</dt><dd>${escapeHtml(scale)}</dd></div>
|
|
184
|
+
<div><dt>Default dimensions</dt><dd>${escapeHtml(formatDimensions(baseline))}</dd></div>
|
|
185
|
+
<div><dt>Scaled dimensions</dt><dd>${escapeHtml(formatDimensions(scaled))}</dd></div>
|
|
186
|
+
</dl>
|
|
187
|
+
<div class="comparison-grid" role="group" aria-label="Default and large-text screenshots">
|
|
188
|
+
${renderScreenshotFigure(evidence.beforeScreenshot, "Default profile (before)", `Default profile screenshot for ${run.projectName} at ${route}`)}
|
|
189
|
+
${renderScreenshotFigure(evidence.afterScreenshot, `Large text at ${scale} (after)`, `Large-text profile screenshot for ${run.projectName} at ${route}`)}
|
|
190
|
+
</div>
|
|
191
|
+
</section>`;
|
|
192
|
+
}
|
|
193
|
+
export function renderProfileEvidenceSection(runs, loaded) {
|
|
194
|
+
const blocks = [];
|
|
195
|
+
for (const run of runs) {
|
|
196
|
+
const evidence = loaded.get(run.runId);
|
|
197
|
+
if (!evidence)
|
|
198
|
+
continue;
|
|
199
|
+
if (evidence.focusSequence) {
|
|
200
|
+
blocks.push(renderFocusSequenceBlock(run, evidence.focusSequence));
|
|
201
|
+
}
|
|
202
|
+
if (run.profile === "large-text") {
|
|
203
|
+
blocks.push(renderLargeTextComparisonBlock(run, evidence));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (blocks.length === 0) {
|
|
207
|
+
return `<p class="muted">No structured profile evidence was recorded for this audit.</p>`;
|
|
208
|
+
}
|
|
209
|
+
return `<div class="profile-evidence-list">${blocks.join("")}</div>`;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=profile-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-evidence.js","sourceRoot":"","sources":["../src/profile-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuditExecutionResult } from "@a11yst/types";
|
|
2
|
+
/**
|
|
3
|
+
* Validates the persisted result boundary used by reporters.
|
|
4
|
+
*
|
|
5
|
+
* The reporter intentionally validates the document structure it consumes,
|
|
6
|
+
* rather than duplicating every configuration contract from @a11yst/types.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateAuditResultDocument(value: unknown): AuditExecutionResult;
|
|
9
|
+
export declare function readAuditResult(path: string): Promise<AuditExecutionResult>;
|
|
10
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAiC1D;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,GACb,oBAAoB,CA8DtB;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,CAAC,CAW/B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function requireRecord(value, path) {
|
|
6
|
+
if (!isRecord(value)) {
|
|
7
|
+
throw new TypeError(`Invalid audit result: ${path} must be an object.`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function requireArray(value, path) {
|
|
11
|
+
if (!Array.isArray(value)) {
|
|
12
|
+
throw new TypeError(`Invalid audit result: ${path} must be an array.`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function requireString(value, path) {
|
|
16
|
+
if (typeof value !== "string") {
|
|
17
|
+
throw new TypeError(`Invalid audit result: ${path} must be a string.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function requireNumber(value, path) {
|
|
21
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
22
|
+
throw new TypeError(`Invalid audit result: ${path} must be a finite number.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates the persisted result boundary used by reporters.
|
|
27
|
+
*
|
|
28
|
+
* The reporter intentionally validates the document structure it consumes,
|
|
29
|
+
* rather than duplicating every configuration contract from @a11yst/types.
|
|
30
|
+
*/
|
|
31
|
+
export function validateAuditResultDocument(value) {
|
|
32
|
+
requireRecord(value, "document");
|
|
33
|
+
if (value.schemaVersion !== "1") {
|
|
34
|
+
const received = typeof value.schemaVersion === "string"
|
|
35
|
+
? `"${value.schemaVersion}"`
|
|
36
|
+
: typeof value.schemaVersion;
|
|
37
|
+
throw new TypeError(`Incompatible audit result schemaVersion ${received}; @a11yst/reporters supports only schemaVersion "1".`);
|
|
38
|
+
}
|
|
39
|
+
requireRecord(value.summary, "summary");
|
|
40
|
+
for (const key of [
|
|
41
|
+
"durationMs",
|
|
42
|
+
"plannedRuns",
|
|
43
|
+
"completedRuns",
|
|
44
|
+
"skippedRuns",
|
|
45
|
+
"failedRuns",
|
|
46
|
+
"findingCount",
|
|
47
|
+
]) {
|
|
48
|
+
requireNumber(value.summary[key], `summary.${key}`);
|
|
49
|
+
}
|
|
50
|
+
requireRecord(value.summary.findingsBySeverity, "summary.findingsBySeverity");
|
|
51
|
+
for (const severity of ["critical", "high", "medium", "minor"]) {
|
|
52
|
+
requireNumber(value.summary.findingsBySeverity[severity], `summary.findingsBySeverity.${severity}`);
|
|
53
|
+
}
|
|
54
|
+
requireArray(value.runs, "runs");
|
|
55
|
+
value.runs.forEach((run, index) => {
|
|
56
|
+
requireRecord(run, `runs[${index}]`);
|
|
57
|
+
requireString(run.runId, `runs[${index}].runId`);
|
|
58
|
+
requireString(run.status, `runs[${index}].status`);
|
|
59
|
+
requireNumber(run.durationMs, `runs[${index}].durationMs`);
|
|
60
|
+
requireArray(run.findings, `runs[${index}].findings`);
|
|
61
|
+
requireArray(run.diagnostics, `runs[${index}].diagnostics`);
|
|
62
|
+
});
|
|
63
|
+
requireArray(value.findings, "findings");
|
|
64
|
+
value.findings.forEach((finding, index) => {
|
|
65
|
+
requireRecord(finding, `findings[${index}]`);
|
|
66
|
+
for (const key of [
|
|
67
|
+
"id",
|
|
68
|
+
"fingerprint",
|
|
69
|
+
"source",
|
|
70
|
+
"ruleId",
|
|
71
|
+
"title",
|
|
72
|
+
"severity",
|
|
73
|
+
"projectName",
|
|
74
|
+
"profile",
|
|
75
|
+
]) {
|
|
76
|
+
requireString(finding[key], `findings[${index}].${key}`);
|
|
77
|
+
}
|
|
78
|
+
requireArray(finding.target, `findings[${index}].target`);
|
|
79
|
+
requireArray(finding.standards, `findings[${index}].standards`);
|
|
80
|
+
});
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
export async function readAuditResult(path) {
|
|
84
|
+
let document;
|
|
85
|
+
try {
|
|
86
|
+
document = JSON.parse(await readFile(path, "utf8"));
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
throw new Error(`Unable to read audit result from "${path}": ${message}`, {
|
|
91
|
+
cause: error,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return validateAuditResultDocument(document);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,aAAa,CACpB,KAAc,EACd,IAAY;IAEZ,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CAAC,yBAAyB,IAAI,qBAAqB,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,IAAY;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,SAAS,CAAC,yBAAyB,IAAI,oBAAoB,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,IAAY;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,yBAAyB,IAAI,oBAAoB,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,IAAY;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,yBAAyB,IAAI,2BAA2B,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAc;IAEd,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAEjC,IAAI,KAAK,CAAC,aAAa,KAAK,GAAG,EAAE,CAAC;QAChC,MAAM,QAAQ,GACZ,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ;YACrC,CAAC,CAAC,IAAI,KAAK,CAAC,aAAa,GAAG;YAC5B,CAAC,CAAC,OAAO,KAAK,CAAC,aAAa,CAAC;QACjC,MAAM,IAAI,SAAS,CACjB,2CAA2C,QAAQ,sDAAsD,CAC1G,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI;QAChB,YAAY;QACZ,aAAa;QACb,eAAe;QACf,aAAa;QACb,YAAY;QACZ,cAAc;KACf,EAAE,CAAC;QACF,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;IAC9E,KAAK,MAAM,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;QAC/D,aAAa,CACX,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAC1C,8BAA8B,QAAQ,EAAE,CACzC,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QAChC,aAAa,CAAC,GAAG,EAAE,QAAQ,KAAK,GAAG,CAAC,CAAC;QACrC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC;QACjD,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC;QACnD,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,KAAK,cAAc,CAAC,CAAC;QAC3D,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,YAAY,CAAC,CAAC;QACtD,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,KAAK,eAAe,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACzC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACxC,aAAa,CAAC,OAAO,EAAE,YAAY,KAAK,GAAG,CAAC,CAAC;QAC7C,KAAK,MAAM,GAAG,IAAI;YAChB,IAAI;YACJ,aAAa;YACb,QAAQ;YACR,QAAQ;YACR,OAAO;YACP,UAAU;YACV,aAAa;YACb,SAAS;SACV,EAAE,CAAC;YACF,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,YAAY,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK,UAAU,CAAC,CAAC;QAC1D,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,YAAY,KAAK,aAAa,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,OAAO,KAAwC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY;IAEZ,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,MAAM,OAAO,EAAE,EAAE;YACxE,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IACD,OAAO,2BAA2B,CAAC,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a11yst/reporters",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Static report generation for a11yst audit results",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@a11yst/sarif": "1.0.0",
|
|
20
|
+
"@a11yst/types": "1.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.10.2",
|
|
24
|
+
"typescript": "^5.7.2"
|
|
25
|
+
},
|
|
26
|
+
"license": "MPL-2.0",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/angelabenavente/a11yst.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/angelabenavente/a11yst#readme",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/angelabenavente/a11yst/issues"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json",
|
|
40
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
41
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
42
|
+
}
|
|
43
|
+
}
|