eaa-kit 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 +21 -0
- package/README.md +102 -0
- package/dist/astro/index.d.ts +90 -0
- package/dist/astro/index.js +48 -0
- package/dist/audit/runners/worker.d.ts +1 -0
- package/dist/audit/runners/worker.js +27 -0
- package/dist/audit-6gbV0Zjd.js +582 -0
- package/dist/audit-VtuUOuyX.js +2 -0
- package/dist/baseline-DQTnNlc4.js +169 -0
- package/dist/baseline-Itspu3-Y.js +2 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +259 -0
- package/dist/escape-Dm1o_RAk.js +21 -0
- package/dist/fingerprint-DRoneAjj.js +20 -0
- package/dist/html-BLEuzep6.js +337 -0
- package/dist/impact-DvgBjupx.js +32 -0
- package/dist/impact-EEB9ZXmC.d.ts +7 -0
- package/dist/index.d.ts +264 -0
- package/dist/index.js +2 -0
- package/dist/jsdom-BEu6Ra_2.js +163 -0
- package/dist/jsdom-C6dIyaxN.js +3 -0
- package/dist/json-1ESNIiHY.js +139 -0
- package/dist/playwright-BfWuTG_u.js +235 -0
- package/dist/pool-DixLeu8L.js +188 -0
- package/dist/render-K9KxDDSA.js +774 -0
- package/dist/result-2aZPfM8w.js +168 -0
- package/dist/sarif-eSCuI0eX.js +192 -0
- package/dist/statement/templates/at.de.md +127 -0
- package/dist/statement/templates/at.en.md +121 -0
- package/dist/statement/templates/ch.de.md +138 -0
- package/dist/statement/templates/ch.en.md +135 -0
- package/dist/statement/templates/de.de.md +129 -0
- package/dist/statement/templates/de.en.md +125 -0
- package/dist/version-B3v4rNoG.js +15 -0
- package/package.json +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kristof Kolity
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# eaa-kit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/likeBloodMoon/eaa-kit/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Build-time WCAG 2.2 AA auditor and EU accessibility statement generator for static sites,
|
|
6
|
+
aimed at freelancers and small agencies in the DACH region who have to comply with the
|
|
7
|
+
European Accessibility Act (in force since 28 June 2025), the BFSG in Germany and the BaFG
|
|
8
|
+
in Austria.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx eaa-kit audit ./dist # WCAG 2.2 AA report over built HTML
|
|
12
|
+
npx eaa-kit statement # Barrierefreiheitserklärung from eaa.config
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **Not legal advice.** eaa-kit reports what an automated engine can and cannot determine
|
|
16
|
+
> about your markup. Automated testing catches a minority of accessibility barriers; it is
|
|
17
|
+
> a floor, not a certificate.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add -D eaa-kit # npm i -D eaa-kit
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Node 22.22 or newer. jsdom, which the browserless engine is built on, does not support
|
|
26
|
+
Node 20.
|
|
27
|
+
|
|
28
|
+
## What it does
|
|
29
|
+
|
|
30
|
+
**Audits your build.** Globs the HTML out of `./dist`, parses it with jsdom and runs
|
|
31
|
+
axe-core against it. No Chromium download, fast enough for CI, and it never fetches
|
|
32
|
+
anything or executes your site's JavaScript. `--browser` swaps in real Chromium for the
|
|
33
|
+
rules that need layout and CSS.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
eaa-kit audit ./dist --fail-on serious
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Writes the statement.** A Barrierefreiheitserklärung from one config file, in German or
|
|
40
|
+
English, as Markdown or HTML, naming the statute and supervisory body of Austria,
|
|
41
|
+
Switzerland or Germany — and optionally listing the barriers a real audit found.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
eaa-kit statement --output src/content/a11y.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Fails only on what is new.** The first run on a site that already exists finds
|
|
48
|
+
everything at once. A baseline records what is already wrong so the build fails on
|
|
49
|
+
regressions instead, without ever letting an accepted barrier look like a passing one.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
eaa-kit baseline ./dist
|
|
53
|
+
eaa-kit audit ./dist --baseline eaa-baseline.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Reports in four shapes**: a console report for whoever ran it, JSON for other tools,
|
|
57
|
+
SARIF for GitHub code scanning, and a self-contained HTML page for the client whose site
|
|
58
|
+
it is.
|
|
59
|
+
|
|
60
|
+
**Runs in your build**, as an [Astro integration](docs/integrations.md#astro-integration)
|
|
61
|
+
or the bundled [GitHub Action](docs/integrations.md#github-actions).
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
| | |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| [Auditing a build](docs/audit.md) | The `audit` command, both engines, exit codes, and what an automated run can and cannot tell you |
|
|
68
|
+
| [The statement command](docs/statement.md) | The config file, the three countries, and filling a statement from audit results |
|
|
69
|
+
| [Baselines](docs/baseline.md) | Adopting the tool on a site that already has violations |
|
|
70
|
+
| [Report formats](docs/reports.md) | The JSON contract, SARIF, and the HTML report |
|
|
71
|
+
| [Integrations](docs/integrations.md) | Astro and GitHub Actions |
|
|
72
|
+
|
|
73
|
+
Complete generated output for every format is checked in under [examples/](examples).
|
|
74
|
+
|
|
75
|
+
## What it will not tell you
|
|
76
|
+
|
|
77
|
+
The two things worth knowing before you rely on any of it:
|
|
78
|
+
|
|
79
|
+
**Automated testing finds a minority of barriers.** It cannot judge whether alternative
|
|
80
|
+
text is accurate, whether a page makes sense in reading order, or whether a form can
|
|
81
|
+
actually be completed with a screen reader. A clean report means nothing was found by this
|
|
82
|
+
engine, which is not the same as a site being accessible — and none of the four output
|
|
83
|
+
formats will say otherwise on your behalf.
|
|
84
|
+
|
|
85
|
+
**The browserless engine cannot decide everything.** jsdom has no layout, so rules that
|
|
86
|
+
depend on rendering — colour contrast, target size, computed overflow — cannot be
|
|
87
|
+
evaluated. axe-core does not know that and will report some of them as *passing*. eaa-kit
|
|
88
|
+
never passes those on: they are reported as **not evaluated**, with the reason, whatever
|
|
89
|
+
axe-core said about them. See [what the browserless engine can and cannot tell
|
|
90
|
+
you](docs/audit.md#what-the-browserless-engine-can-and-cannot-tell-you).
|
|
91
|
+
|
|
92
|
+
The same applies to the statement: it says what you told it. eaa-kit cannot check whether
|
|
93
|
+
those claims are true, and a statement claiming full conformance for a site that is not
|
|
94
|
+
conformant is worse than no statement at all.
|
|
95
|
+
|
|
96
|
+
## Changelog
|
|
97
|
+
|
|
98
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { t as ImpactLevel } from "../impact-EEB9ZXmC.js";
|
|
2
|
+
//#region src/cli/audit.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The engines and the machine-readable reporters are imported where they are
|
|
5
|
+
* used, not at the top of the file.
|
|
6
|
+
*
|
|
7
|
+
* jsdom costs 630 ms to load and axe-core another 94 ms, and a static import
|
|
8
|
+
* here charges that to every invocation — `eaa-kit statement`, `--help` and a
|
|
9
|
+
* mistyped flag included, none of which parse a single page. The audit path
|
|
10
|
+
* pays the same cost either way, a few milliseconds later.
|
|
11
|
+
*/
|
|
12
|
+
declare const OUTPUT_FORMATS: readonly ['console', 'json', 'sarif', 'html'];
|
|
13
|
+
type OutputFormat = (typeof OUTPUT_FORMATS)[number];
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/astro/index.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* An Astro integration that audits the build Astro just produced.
|
|
18
|
+
*
|
|
19
|
+
* `astro build` already knows where the output went and when it finished, which
|
|
20
|
+
* is the one moment a build-time auditor wants. Wiring the CLI into a project's
|
|
21
|
+
* scripts works, but it means remembering to, and it means the audit runs in a
|
|
22
|
+
* separate step that is easy to drop from a pipeline when it goes red.
|
|
23
|
+
*
|
|
24
|
+
* The Astro types are described structurally here rather than imported. Astro is
|
|
25
|
+
* an optional peer dependency, exactly like Playwright, and a published .d.ts
|
|
26
|
+
* that referenced `astro` would fail to typecheck for everyone who installed
|
|
27
|
+
* this package without it. The shapes below are checked against the real
|
|
28
|
+
* `AstroIntegration` type at compile time in the tests, and the integration is
|
|
29
|
+
* driven by an actual `astro build` there, so the structural copy cannot drift
|
|
30
|
+
* from the API it stands in for.
|
|
31
|
+
*/
|
|
32
|
+
/** The part of Astro's integration logger this uses. */
|
|
33
|
+
interface AstroLoggerLike {
|
|
34
|
+
info(message: string): void;
|
|
35
|
+
warn(message: string): void;
|
|
36
|
+
error(message: string): void;
|
|
37
|
+
}
|
|
38
|
+
interface BuildDoneOptions {
|
|
39
|
+
/** Where Astro wrote the build. */
|
|
40
|
+
dir: URL;
|
|
41
|
+
logger: AstroLoggerLike;
|
|
42
|
+
}
|
|
43
|
+
interface AstroIntegrationLike {
|
|
44
|
+
name: string;
|
|
45
|
+
hooks: {
|
|
46
|
+
'astro:build:done'?: (options: BuildDoneOptions) => void | Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
interface EaaKitIntegrationOptions {
|
|
50
|
+
/** Lowest impact that fails the build. Defaults to 'serious'. */
|
|
51
|
+
failOn?: ImpactLevel;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a failing audit fails the build. Defaults to true.
|
|
54
|
+
*
|
|
55
|
+
* The CLI has no equivalent because a shell can ignore an exit code; a build
|
|
56
|
+
* hook cannot. Turning this off is for the first week of adopting the tool on
|
|
57
|
+
* an existing site — after that, a baseline is the honest way to go green,
|
|
58
|
+
* because it records what is wrong instead of hiding it.
|
|
59
|
+
*/
|
|
60
|
+
failBuild?: boolean;
|
|
61
|
+
/** Skip the audit entirely. For turning it off by environment. */
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
include?: string[];
|
|
64
|
+
exclude?: string[];
|
|
65
|
+
/** Audit pages under their real site URL instead of file://. */
|
|
66
|
+
baseUrl?: string;
|
|
67
|
+
/** Audit in real Chromium. Needs the playwright peer. */
|
|
68
|
+
browser?: boolean;
|
|
69
|
+
/** Worker threads for the browserless engine. */
|
|
70
|
+
concurrency?: number;
|
|
71
|
+
/** Accept the violations recorded in this file; fail only on new ones. */
|
|
72
|
+
baseline?: string;
|
|
73
|
+
/** Write a report as well as printing one. */
|
|
74
|
+
format?: OutputFormat;
|
|
75
|
+
/** Where to write it. Required for `format` to do anything useful. */
|
|
76
|
+
output?: string;
|
|
77
|
+
}
|
|
78
|
+
declare class AstroAuditError extends Error {
|
|
79
|
+
readonly name = "AstroAuditError";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Audit the build in `astro:build:done`, and fail the build if it does not pass.
|
|
83
|
+
*
|
|
84
|
+
* Failing by default is the point: an auditor that only ever prints is one
|
|
85
|
+
* nobody reads. `failBuild: false` is there for the week it takes to adopt it,
|
|
86
|
+
* and `baseline` is the answer after that.
|
|
87
|
+
*/
|
|
88
|
+
declare function eaaKit(options?: EaaKitIntegrationOptions): AstroIntegrationLike;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { AstroAuditError, AstroIntegrationLike, AstroLoggerLike, BuildDoneOptions, EaaKitIntegrationOptions, eaaKit as default };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
//#region src/astro/index.ts
|
|
3
|
+
var AstroAuditError = class extends Error {
|
|
4
|
+
name = "AstroAuditError";
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Audit the build in `astro:build:done`, and fail the build if it does not pass.
|
|
8
|
+
*
|
|
9
|
+
* Failing by default is the point: an auditor that only ever prints is one
|
|
10
|
+
* nobody reads. `failBuild: false` is there for the week it takes to adopt it,
|
|
11
|
+
* and `baseline` is the answer after that.
|
|
12
|
+
*/
|
|
13
|
+
function eaaKit(options = {}) {
|
|
14
|
+
return {
|
|
15
|
+
name: "eaa-kit",
|
|
16
|
+
hooks: { "astro:build:done": async ({ dir, logger }) => {
|
|
17
|
+
if (options.enabled === false) {
|
|
18
|
+
logger.info("skipped (enabled: false)");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const { runAuditCommand } = await import("../audit-VtuUOuyX.js");
|
|
22
|
+
const { exitCode } = await runAuditCommand(fileURLToPath(dir), {
|
|
23
|
+
...options.failOn ? { failOn: options.failOn } : {},
|
|
24
|
+
...options.include ? { include: options.include } : {},
|
|
25
|
+
...options.exclude ? { exclude: options.exclude } : {},
|
|
26
|
+
...options.baseUrl ? { baseUrl: options.baseUrl } : {},
|
|
27
|
+
...options.browser ? { browser: true } : {},
|
|
28
|
+
...options.concurrency === void 0 ? {} : { concurrency: options.concurrency },
|
|
29
|
+
...options.baseline ? { baseline: options.baseline } : {},
|
|
30
|
+
...options.format ? { format: options.format } : {},
|
|
31
|
+
...options.output ? { output: options.output } : {}
|
|
32
|
+
});
|
|
33
|
+
if (exitCode === 0) {
|
|
34
|
+
logger.info("no violations at or above the threshold");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const message = exitCode === 2 ? "the audit could not be completed, so this build was not checked" : "accessibility violations at or above the threshold";
|
|
38
|
+
if (options.failBuild === false) {
|
|
39
|
+
logger.warn(`${message} (failBuild: false, so the build continues)`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
logger.error(message);
|
|
43
|
+
throw new AstroAuditError(`eaa-kit: ${message}`);
|
|
44
|
+
} }
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { AstroAuditError, eaaKit as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { n as auditPage } from "../../jsdom-BEu6Ra_2.js";
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
//#region src/audit/runners/worker.ts
|
|
4
|
+
/**
|
|
5
|
+
* One audit worker: a thread that owns its own jsdom and axe-core, and audits
|
|
6
|
+
* whatever page the pool hands it.
|
|
7
|
+
*
|
|
8
|
+
* It exists as a separate entry point rather than as inline source passed to
|
|
9
|
+
* `new Worker(…, { eval: true })` because the thing it has to import — the
|
|
10
|
+
* jsdom runner — sits at a different path when running from `src` than it does
|
|
11
|
+
* in the bundle, and a string of code cannot be resolved against either.
|
|
12
|
+
*
|
|
13
|
+
* The protocol is deliberately one message each way: a CollectedPage in, the
|
|
14
|
+
* PageAudit it produced out, both plain data that structured-clone handles.
|
|
15
|
+
* `auditPage` turns a page it cannot audit into a PageAudit carrying an
|
|
16
|
+
* `error`, so a failure is an ordinary reply rather than a special case. Only
|
|
17
|
+
* something catastrophic — the thread running out of memory — reaches the
|
|
18
|
+
* pool's error handler instead.
|
|
19
|
+
*/
|
|
20
|
+
const port = parentPort;
|
|
21
|
+
if (!port) throw new Error("eaa-kit audit worker was started outside a worker thread");
|
|
22
|
+
const options = workerData ?? {};
|
|
23
|
+
port.on("message", async (page) => {
|
|
24
|
+
port.postMessage(await auditPage(page, options));
|
|
25
|
+
});
|
|
26
|
+
//#endregion
|
|
27
|
+
export {};
|