@holmdigital/engine 1.4.12 → 2.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 +59 -59
- package/README.md +198 -158
- package/dist/{chunk-YWRTSIUX.mjs → chunk-32WU5BD6.mjs} +151 -7
- package/dist/{chunk-FFTRXRZU.mjs → chunk-ZO2XNHHT.mjs} +177 -4
- package/dist/cli/index.js +591 -45
- package/dist/cli/index.mjs +194 -56
- package/dist/github-actions-CGWJSLMB.mjs +24 -0
- package/dist/{i18n-WJLEZT5A.mjs → i18n-VBP5BVTT.mjs} +1 -1
- package/dist/index.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +339 -9
- package/dist/index.mjs +7 -3
- package/dist/junit-generator-FK3GE5E4.mjs +43 -0
- package/package.json +83 -79
|
@@ -10,6 +10,8 @@ var en_default = {
|
|
|
10
10
|
pdf_saved: "PDF Report saved to {path}",
|
|
11
11
|
scan_failed: "Scan failed",
|
|
12
12
|
critical_failure: "\nCI/CD Failure: Critical regulatory violations found.",
|
|
13
|
+
generating_statement: "Generating Accessibility Statement...",
|
|
14
|
+
statement_saved: "Accessibility Statement saved to {path}",
|
|
13
15
|
score: "\nCompliance Score: {score}/100",
|
|
14
16
|
status: "Compliance Status: {status}",
|
|
15
17
|
not_compliant: " (Not compliant with legal requirements)",
|
|
@@ -17,7 +19,8 @@ var en_default = {
|
|
|
17
19
|
prescriptive_fix: "\n\u{1F4A1} Prescriptive Fix:",
|
|
18
20
|
use_component: "Use component: {component}",
|
|
19
21
|
pseudo_tests: "\n\u{1F9EC} Generating Pseudo-Automation Tests...\n",
|
|
20
|
-
test_for: "Test for {ruleId}:"
|
|
22
|
+
test_for: "Test for {ruleId}:",
|
|
23
|
+
junit_saved: "JUnit XML report saved to {path}"
|
|
21
24
|
},
|
|
22
25
|
report: {
|
|
23
26
|
title: "Accessibility Report - {url}",
|
|
@@ -47,6 +50,8 @@ var sv_default = {
|
|
|
47
50
|
pdf_saved: "PDF-rapport sparad till {path}",
|
|
48
51
|
scan_failed: "Skanning misslyckades",
|
|
49
52
|
critical_failure: "\nCI/CD Fel: Kritiska regulatoriska brister hittades.",
|
|
53
|
+
generating_statement: "Genererar tillg\xE4nglighetsredog\xF6relse...",
|
|
54
|
+
statement_saved: "Tillg\xE4nglighetsredog\xF6relse sparad till {path}",
|
|
50
55
|
score: "\nEfterlevnadspo\xE4ng: {score}/100",
|
|
51
56
|
status: "Efterlevnadsstatus: {status}",
|
|
52
57
|
not_compliant: " (Uppfyller ej legala krav)",
|
|
@@ -54,7 +59,8 @@ var sv_default = {
|
|
|
54
59
|
prescriptive_fix: "\n\u{1F4A1} Preskriptiv L\xF6sning:",
|
|
55
60
|
use_component: "Anv\xE4nd komponent: {component}",
|
|
56
61
|
pseudo_tests: "\n\u{1F9EC} Genererar Pseudo-automationstester...\n",
|
|
57
|
-
test_for: "Test f\xF6r {ruleId}:"
|
|
62
|
+
test_for: "Test f\xF6r {ruleId}:",
|
|
63
|
+
junit_saved: "JUnit XML-rapport sparad till {path}"
|
|
58
64
|
},
|
|
59
65
|
report: {
|
|
60
66
|
title: "Tillg\xE4nglighetsrapport - {url}",
|
|
@@ -84,6 +90,8 @@ var de_default = {
|
|
|
84
90
|
pdf_saved: "PDF-Bericht gespeichert unter {path}",
|
|
85
91
|
scan_failed: "Scan fehlgeschlagen",
|
|
86
92
|
critical_failure: "\nCI/CD Fehler: Kritische regulatorische Verst\xF6\xDFe gefunden.",
|
|
93
|
+
generating_statement: "Generiere Barrierefreiheitserkl\xE4rung...",
|
|
94
|
+
statement_saved: "Barrierefreiheitserkl\xE4rung gespeichert unter {path}",
|
|
87
95
|
score: "\nKonformit\xE4tsbewertung: {score}/100",
|
|
88
96
|
status: "Konformit\xE4tsstatus: {status}",
|
|
89
97
|
not_compliant: " (Nicht konform mit gesetzlichen Anforderungen - BITV 2.0)",
|
|
@@ -91,7 +99,8 @@ var de_default = {
|
|
|
91
99
|
prescriptive_fix: "\n\u{1F4A1} Vorschriftsm\xE4\xDFige L\xF6sung:",
|
|
92
100
|
use_component: "Verwenden Sie die Komponente: {component}",
|
|
93
101
|
pseudo_tests: "\n\u{1F9EC} Generiere Pseudo-Automatisierungstests...\n",
|
|
94
|
-
test_for: "Test f\xFCr {ruleId}:"
|
|
102
|
+
test_for: "Test f\xFCr {ruleId}:",
|
|
103
|
+
junit_saved: "JUnit XML-Bericht gespeichert unter {path}"
|
|
95
104
|
},
|
|
96
105
|
report: {
|
|
97
106
|
title: "Barrierefreiheitsbericht - {url}",
|
|
@@ -121,6 +130,8 @@ var fr_default = {
|
|
|
121
130
|
pdf_saved: "Rapport PDF enregistr\xE9 sous {path}",
|
|
122
131
|
scan_failed: "\xC9chec du scan",
|
|
123
132
|
critical_failure: "\n\xC9chec CI/CD : Violations r\xE9glementaires critiques d\xE9tect\xE9es.",
|
|
133
|
+
generating_statement: "G\xE9n\xE9ration de la d\xE9claration d'accessibilit\xE9...",
|
|
134
|
+
statement_saved: "D\xE9claration d'accessibilit\xE9 enregistr\xE9e sous {path}",
|
|
124
135
|
score: "\nScore de Conformit\xE9 : {score}/100",
|
|
125
136
|
status: "Statut de Conformit\xE9 : {status}",
|
|
126
137
|
not_compliant: " (Non conforme aux exigences l\xE9gales - RGAA)",
|
|
@@ -128,7 +139,8 @@ var fr_default = {
|
|
|
128
139
|
prescriptive_fix: "\n\u{1F4A1} Solution Prescriptive :",
|
|
129
140
|
use_component: "Utilisez le composant : {component}",
|
|
130
141
|
pseudo_tests: "\n\u{1F9EC} G\xE9n\xE9ration de tests de pseudo-automatisation...\n",
|
|
131
|
-
test_for: "Test pour {ruleId} :"
|
|
142
|
+
test_for: "Test pour {ruleId} :",
|
|
143
|
+
junit_saved: "Rapport XML JUnit enregistr\xE9 sous {path}"
|
|
132
144
|
},
|
|
133
145
|
report: {
|
|
134
146
|
title: "Rapport d'Accessibilit\xE9 - {url}",
|
|
@@ -158,6 +170,8 @@ var es_default = {
|
|
|
158
170
|
pdf_saved: "Informe PDF guardado en {path}",
|
|
159
171
|
scan_failed: "Escaneo fallido",
|
|
160
172
|
critical_failure: "\nFallo de CI/CD: Se encontraron violaciones regulatorias cr\xEDticas.",
|
|
173
|
+
generating_statement: "Generando Declaraci\xF3n de Accesibilidad...",
|
|
174
|
+
statement_saved: "Declaraci\xF3n de Accesibilidad guardada en {path}",
|
|
161
175
|
score: "\nPuntuaci\xF3n de Cumplimiento: {score}/100",
|
|
162
176
|
status: "Estado de Cumplimiento: {status}",
|
|
163
177
|
not_compliant: " (No cumple con los requisitos legales - UNE 139803)",
|
|
@@ -165,7 +179,8 @@ var es_default = {
|
|
|
165
179
|
prescriptive_fix: "\n\u{1F4A1} Soluci\xF3n Prescriptiva:",
|
|
166
180
|
use_component: "Use el componente: {component}",
|
|
167
181
|
pseudo_tests: "\n\u{1F9EC} Generando pruebas de pseudo-automatizaci\xF3n...\n",
|
|
168
|
-
test_for: "Prueba para {ruleId}:"
|
|
182
|
+
test_for: "Prueba para {ruleId}:",
|
|
183
|
+
junit_saved: "Informe JUnit XML guardado en {path}"
|
|
169
184
|
},
|
|
170
185
|
report: {
|
|
171
186
|
title: "Informe de Accesibilidad - {url}",
|
|
@@ -183,6 +198,126 @@ var es_default = {
|
|
|
183
198
|
}
|
|
184
199
|
};
|
|
185
200
|
|
|
201
|
+
// src/locales/fi.json
|
|
202
|
+
var fi_default = {
|
|
203
|
+
cli: {
|
|
204
|
+
title: "\n\u{1F310} HolmDigital S\xE4\xE4ntelyskanneri\n",
|
|
205
|
+
scanning: "Skannataan {url}...\n",
|
|
206
|
+
initializing: "Alustetaan skanneria...",
|
|
207
|
+
analyzing: "Analysoidaan DOM & Shadow DOM...",
|
|
208
|
+
complete: "Skannaus valmis!",
|
|
209
|
+
generating_pdf: "Luodaan PDF-raporttia...",
|
|
210
|
+
pdf_saved: "PDF-raportti tallennettu kohteeseen {path}",
|
|
211
|
+
scan_failed: "Skannaus ep\xE4onnistui",
|
|
212
|
+
critical_failure: "\nCI/CD Virhe: Kriittisi\xE4 s\xE4\xE4ntelyrikkomuksia havaittu.",
|
|
213
|
+
generating_statement: "Luodaan saavutettavuusselostetta...",
|
|
214
|
+
statement_saved: "Saavutettavuusseloste tallennettu kohteeseen {path}",
|
|
215
|
+
score: "\nVaatimustenmukaisuus: {score}/100",
|
|
216
|
+
status: "Tila: {status}",
|
|
217
|
+
not_compliant: " (Ei t\xE4yt\xE4 lakis\xE4\xE4teisi\xE4 vaatimuksia - Laki digitaalisten palvelujen tarjoamisesta)",
|
|
218
|
+
viewport: "N\xE4kym\xE4: {width}x{height}",
|
|
219
|
+
prescriptive_fix: "\n\u{1F4A1} Ehdotettu korjaus:",
|
|
220
|
+
use_component: "K\xE4yt\xE4 komponenttia: {component}",
|
|
221
|
+
pseudo_tests: "\n\u{1F9EC} Luodaan Pseudo-automaatiotestej\xE4...\n",
|
|
222
|
+
test_for: "Testi {ruleId}:lle:",
|
|
223
|
+
junit_saved: "JUnit XML -raportti tallennettu kohteeseen {path}"
|
|
224
|
+
},
|
|
225
|
+
report: {
|
|
226
|
+
title: "Saavutettavuusraportti - {url}",
|
|
227
|
+
generated: "Luotu: {date}",
|
|
228
|
+
scan_target: "Skannauksen kohde: {url}",
|
|
229
|
+
overall_score: "Kokonaispisteet",
|
|
230
|
+
critical_issues: "Kriittiset ongelmat",
|
|
231
|
+
high_issues: "Vakavat ongelmat",
|
|
232
|
+
total_issues: "Ongelmat yhteens\xE4",
|
|
233
|
+
html_errors: "HTML-virheet",
|
|
234
|
+
detailed_violations: "Yksityiskohtaiset rikkomukset",
|
|
235
|
+
prescriptive_fix: "\u{1F4A1} Ehdotettu korjaus",
|
|
236
|
+
use: "K\xE4yt\xE4",
|
|
237
|
+
footer: "Luonut @holmdigital/engine v0.1.0 \u2022 Standardit: WCAG 2.1 AA, EN 301 549, Digipalvelulaki"
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// src/locales/dk.json
|
|
242
|
+
var dk_default = {
|
|
243
|
+
cli: {
|
|
244
|
+
title: "\n\u{1F310} HolmDigital Regulatorisk Scanner\n",
|
|
245
|
+
scanning: "Scanner {url}...\n",
|
|
246
|
+
initializing: "Initialiserer scanner...",
|
|
247
|
+
analyzing: "Analyserer DOM & Shadow DOM...",
|
|
248
|
+
complete: "Scanning f\xE6rdig!",
|
|
249
|
+
generating_pdf: "Genererer PDF-rapport...",
|
|
250
|
+
pdf_saved: "PDF-rapport gemt i {path}",
|
|
251
|
+
scan_failed: "Scanning mislykkedes",
|
|
252
|
+
critical_failure: "\nCI/CD Fejl: Kritiske regulatoriske overtr\xE6delser fundet.",
|
|
253
|
+
generating_statement: "Genererer tilg\xE6ngelighedserkl\xE6ring...",
|
|
254
|
+
statement_saved: "Tilg\xE6ngelighedserkl\xE6ring gemt i {path}",
|
|
255
|
+
score: "\nOverholdelsesscore: {score}/100",
|
|
256
|
+
status: "Overholdelsesstatus: {status}",
|
|
257
|
+
not_compliant: " (Opfylder ikke lovkrav - Lov om tilg\xE6ngelighed)",
|
|
258
|
+
viewport: "Viewport: {width}x{height}",
|
|
259
|
+
prescriptive_fix: "\n\u{1F4A1} Foresl\xE5et l\xF8sning:",
|
|
260
|
+
use_component: "Brug komponent: {component}",
|
|
261
|
+
pseudo_tests: "\n\u{1F9EC} Genererer Pseudo-automatiseringstests...\n",
|
|
262
|
+
test_for: "Test for {ruleId}:",
|
|
263
|
+
junit_saved: "JUnit XML-rapport gemt i {path}"
|
|
264
|
+
},
|
|
265
|
+
report: {
|
|
266
|
+
title: "Tilg\xE6ngelighedsrapport - {url}",
|
|
267
|
+
generated: "Genereret: {date}",
|
|
268
|
+
scan_target: "Scanningsm\xE5l: {url}",
|
|
269
|
+
overall_score: "Samlet Score",
|
|
270
|
+
critical_issues: "Kritiske Fejl",
|
|
271
|
+
high_issues: "Alvorlige Fejl",
|
|
272
|
+
total_issues: "Totale Fejl",
|
|
273
|
+
html_errors: "HTML Fejl",
|
|
274
|
+
detailed_violations: "Detaljerede Overtr\xE6delser",
|
|
275
|
+
prescriptive_fix: "\u{1F4A1} Foresl\xE5et L\xF8sning",
|
|
276
|
+
use: "Brug",
|
|
277
|
+
footer: "Genereret af @holmdigital/engine v0.1.0 \u2022 Standarder: WCAG 2.1 AA, EN 301 549, Lov om tilg\xE6ngelighed"
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/locales/no.json
|
|
282
|
+
var no_default = {
|
|
283
|
+
cli: {
|
|
284
|
+
title: "\n\u{1F310} HolmDigital Regulatorisk Skanner\n",
|
|
285
|
+
scanning: "Skanner {url}...\n",
|
|
286
|
+
initializing: "Initialiserer skanner...",
|
|
287
|
+
analyzing: "Analyserer DOM & Shadow DOM...",
|
|
288
|
+
complete: "Skanning ferdig!",
|
|
289
|
+
generating_pdf: "Genererer PDF-rapport...",
|
|
290
|
+
pdf_saved: "PDF-rapport lagret i {path}",
|
|
291
|
+
scan_failed: "Skanning mislyktes",
|
|
292
|
+
critical_failure: "\nCI/CD Feil: Kritiske regulatoriske brudd funnet.",
|
|
293
|
+
generating_statement: "Genererer tilgjengelighetserkl\xE6ring...",
|
|
294
|
+
statement_saved: "Tilgjengelighetserkl\xE6ring lagret i {path}",
|
|
295
|
+
score: "\nEtterlevelsespoeng: {score}/100",
|
|
296
|
+
status: "Etterlevelsesstatus: {status}",
|
|
297
|
+
not_compliant: " (Oppfyller ikke lovkrav - Diskriminerings- og tilgjengelighetsloven)",
|
|
298
|
+
viewport: "Visningsomr\xE5de: {width}x{height}",
|
|
299
|
+
prescriptive_fix: "\n\u{1F4A1} Foresl\xE5tt l\xF8sning:",
|
|
300
|
+
use_component: "Bruk komponent: {component}",
|
|
301
|
+
pseudo_tests: "\n\u{1F9EC} Genererer Pseudo-automatiseringstester...\n",
|
|
302
|
+
test_for: "Test for {ruleId}:",
|
|
303
|
+
junit_saved: "JUnit XML-rapport lagret i {path}"
|
|
304
|
+
},
|
|
305
|
+
report: {
|
|
306
|
+
title: "Tilgjengelighetsrapport - {url}",
|
|
307
|
+
generated: "Generert: {date}",
|
|
308
|
+
scan_target: "Skannet m\xE5l: {url}",
|
|
309
|
+
overall_score: "Total Score",
|
|
310
|
+
critical_issues: "Kritiske Feil",
|
|
311
|
+
high_issues: "Alvorlige Feil",
|
|
312
|
+
total_issues: "Totale Feil",
|
|
313
|
+
html_errors: "HTML Feil",
|
|
314
|
+
detailed_violations: "Detaljerte Brudd",
|
|
315
|
+
prescriptive_fix: "\u{1F4A1} Foresl\xE5tt L\xF8sning",
|
|
316
|
+
use: "Bruk",
|
|
317
|
+
footer: "Generert av @holmdigital/engine v0.1.0 \u2022 Standarder: WCAG 2.1 AA, EN 301 549, Uu-tilsynet"
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
186
321
|
// src/locales/nl.json
|
|
187
322
|
var nl_default = {
|
|
188
323
|
cli: {
|
|
@@ -195,6 +330,8 @@ var nl_default = {
|
|
|
195
330
|
pdf_saved: "PDF-rapport opgeslagen op {path}",
|
|
196
331
|
scan_failed: "Scan mislukt",
|
|
197
332
|
critical_failure: "\nCI/CD Fout: Kritieke wettelijke overtredingen gevonden.",
|
|
333
|
+
generating_statement: "Toegankelijkheidsverklaring genereren...",
|
|
334
|
+
statement_saved: "Toegankelijkheidsverklaring opgeslagen op {path}",
|
|
198
335
|
score: "\nNalevingsscore: {score}/100",
|
|
199
336
|
status: "Nalevingsstatus: {status}",
|
|
200
337
|
not_compliant: " (Niet in overeenstemming met wettelijke vereisten)",
|
|
@@ -202,7 +339,8 @@ var nl_default = {
|
|
|
202
339
|
prescriptive_fix: "\n\u{1F4A1} Voorgeschreven oplossing:",
|
|
203
340
|
use_component: "Gebruik component: {component}",
|
|
204
341
|
pseudo_tests: "\n\u{1F9EC} Pseudo-automatiseringstests genereren...\n",
|
|
205
|
-
test_for: "Test voor {ruleId}:"
|
|
342
|
+
test_for: "Test voor {ruleId}:",
|
|
343
|
+
junit_saved: "JUnit XML-rapport opgeslagen op {path}"
|
|
206
344
|
},
|
|
207
345
|
report: {
|
|
208
346
|
title: "Toegankelijkheidsrapport - {url}",
|
|
@@ -228,9 +366,15 @@ var locales = {
|
|
|
228
366
|
fr: fr_default,
|
|
229
367
|
es: es_default,
|
|
230
368
|
nl: nl_default,
|
|
369
|
+
fi: fi_default,
|
|
370
|
+
dk: dk_default,
|
|
371
|
+
no: no_default,
|
|
231
372
|
"en-gb": en_default,
|
|
232
373
|
"en-us": en_default,
|
|
233
|
-
"en-ca": en_default
|
|
374
|
+
"en-ca": en_default,
|
|
375
|
+
"da": dk_default,
|
|
376
|
+
"nb": no_default
|
|
377
|
+
// Norwegian Bokmål alias
|
|
234
378
|
};
|
|
235
379
|
var currentLang = "en";
|
|
236
380
|
function setLanguage(lang) {
|
|
@@ -252,8 +252,8 @@ var RegulatoryScanner = class {
|
|
|
252
252
|
}
|
|
253
253
|
async enrichResults(axeResults) {
|
|
254
254
|
const reports = [];
|
|
255
|
-
const { searchRulesByTags, generateRegulatoryReport } = await import("@holmdigital/standards");
|
|
256
|
-
const { getCurrentLang } = await import("./i18n-
|
|
255
|
+
const { searchRulesByTags, generateRegulatoryReport, getConvergenceRule } = await import("@holmdigital/standards");
|
|
256
|
+
const { getCurrentLang } = await import("./i18n-VBP5BVTT.mjs");
|
|
257
257
|
const lang = getCurrentLang();
|
|
258
258
|
for (const violation of axeResults.violations) {
|
|
259
259
|
let report = generateRegulatoryReport(violation.id, lang);
|
|
@@ -264,6 +264,7 @@ var RegulatoryScanner = class {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
if (report) {
|
|
267
|
+
const fullRule = getConvergenceRule(report.ruleId, lang);
|
|
267
268
|
reports.push({
|
|
268
269
|
...report,
|
|
269
270
|
holmdigitalInsight: {
|
|
@@ -271,6 +272,8 @@ var RegulatoryScanner = class {
|
|
|
271
272
|
reasoning: violation.help
|
|
272
273
|
// Använd Axe's hjälptext som specifik anledning
|
|
273
274
|
},
|
|
275
|
+
// Include legal context from the full rule
|
|
276
|
+
legalContext: fullRule?.legalContext,
|
|
274
277
|
// Attach extra debug info for the CLI
|
|
275
278
|
failingNodes: violation.nodes.map((node) => ({
|
|
276
279
|
html: node.html,
|
|
@@ -347,6 +350,18 @@ var RegulatoryScanner = class {
|
|
|
347
350
|
pageTitle,
|
|
348
351
|
pageLanguage
|
|
349
352
|
};
|
|
353
|
+
const reportsWithContext = reports.filter((r) => r.legalContext);
|
|
354
|
+
const legalSummary = {
|
|
355
|
+
wadApplicable: reportsWithContext.filter(
|
|
356
|
+
(r) => r.legalContext?.appliesTo?.includes("WAD")
|
|
357
|
+
).length,
|
|
358
|
+
eaaApplicable: reportsWithContext.filter(
|
|
359
|
+
(r) => r.legalContext?.appliesTo?.includes("EAA")
|
|
360
|
+
).length,
|
|
361
|
+
eaaDeadlineViolations: reportsWithContext.filter(
|
|
362
|
+
(r) => r.legalContext?.eaaDeadline
|
|
363
|
+
).length
|
|
364
|
+
};
|
|
350
365
|
return {
|
|
351
366
|
url: this.options.url,
|
|
352
367
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -354,7 +369,8 @@ var RegulatoryScanner = class {
|
|
|
354
369
|
reports,
|
|
355
370
|
stats,
|
|
356
371
|
score,
|
|
357
|
-
complianceStatus
|
|
372
|
+
complianceStatus,
|
|
373
|
+
legalSummary
|
|
358
374
|
};
|
|
359
375
|
}
|
|
360
376
|
/**
|
|
@@ -436,8 +452,165 @@ test('${testName}', async ({ page }) => {
|
|
|
436
452
|
}
|
|
437
453
|
};
|
|
438
454
|
|
|
455
|
+
// src/reporting/badge-generator.ts
|
|
456
|
+
var BADGE_COLOR = "00703C";
|
|
457
|
+
var BADGE_BASE_URL = "https://img.shields.io/badge/HolmDigital_Engine";
|
|
458
|
+
function generateBadgeUrl(score) {
|
|
459
|
+
if (score !== 100) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
return `${BADGE_BASE_URL}-100%25-${BADGE_COLOR}?style=flat-square`;
|
|
463
|
+
}
|
|
464
|
+
function generateBadgeMarkdown(score) {
|
|
465
|
+
const url = generateBadgeUrl(score);
|
|
466
|
+
if (!url) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
return ``;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/reporting/statement-generator.ts
|
|
473
|
+
import React from "react";
|
|
474
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
475
|
+
import { AccessibilityStatement } from "@holmdigital/components";
|
|
476
|
+
import fs from "fs/promises";
|
|
477
|
+
import path from "path";
|
|
478
|
+
async function generateStatementContent(result, lang = "en", format = "html", metadata) {
|
|
479
|
+
let complianceLevel = "full";
|
|
480
|
+
if (result.stats.critical > 0 || result.legalSummary && result.legalSummary.eaaDeadlineViolations > 0) {
|
|
481
|
+
complianceLevel = "non-compliant";
|
|
482
|
+
} else if (result.score < 100) {
|
|
483
|
+
complianceLevel = "partial";
|
|
484
|
+
}
|
|
485
|
+
const issuesMap = /* @__PURE__ */ new Map();
|
|
486
|
+
result.reports.forEach((report) => {
|
|
487
|
+
if (!issuesMap.has(report.ruleId)) {
|
|
488
|
+
const issueText = `${report.ruleId} (${report.wcagCriteria})`;
|
|
489
|
+
issuesMap.set(report.ruleId, issueText);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
const nonComplianceItems = Array.from(issuesMap.values());
|
|
493
|
+
let country = metadata?.country || "SE";
|
|
494
|
+
if (!metadata?.country) {
|
|
495
|
+
if (result.url.endsWith(".no")) country = "NO";
|
|
496
|
+
if (result.url.endsWith(".dk")) country = "DK";
|
|
497
|
+
if (result.url.endsWith(".fi")) country = "FI";
|
|
498
|
+
if (result.url.endsWith(".de")) country = "DE";
|
|
499
|
+
}
|
|
500
|
+
const sector = "public";
|
|
501
|
+
let logoUrl;
|
|
502
|
+
try {
|
|
503
|
+
const possiblePaths = [
|
|
504
|
+
path.join(process.cwd(), "src/assets/logo.jpg"),
|
|
505
|
+
// run from package root
|
|
506
|
+
path.join(process.cwd(), "packages/engine/src/assets/logo.jpg")
|
|
507
|
+
// run from monorepo root
|
|
508
|
+
];
|
|
509
|
+
for (const p of possiblePaths) {
|
|
510
|
+
try {
|
|
511
|
+
const logoBuffer = await fs.readFile(p);
|
|
512
|
+
logoUrl = `data:image/jpeg;base64,${logoBuffer.toString("base64")}`;
|
|
513
|
+
break;
|
|
514
|
+
} catch (e) {
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} catch (e) {
|
|
518
|
+
console.warn("Could not load logo.jpg", e);
|
|
519
|
+
}
|
|
520
|
+
const props = {
|
|
521
|
+
country,
|
|
522
|
+
sector,
|
|
523
|
+
organizationName: metadata?.organizationName || new URL(result.url).hostname.replace(/^www\./, ""),
|
|
524
|
+
websiteUrl: result.url,
|
|
525
|
+
complianceLevel,
|
|
526
|
+
lastReviewDate: /* @__PURE__ */ new Date(),
|
|
527
|
+
assessmentDate: /* @__PURE__ */ new Date(),
|
|
528
|
+
evaluationMethod: lang === "sv" ? "Automatiserad granskning via @holmdigital/engine" : "Automated Scan via @holmdigital/engine",
|
|
529
|
+
generatorTool: {
|
|
530
|
+
name: "HolmDigital Regulatory Engine",
|
|
531
|
+
url: "https://holmdigital.se"
|
|
532
|
+
},
|
|
533
|
+
logoUrl,
|
|
534
|
+
contactEmail: metadata?.contactEmail || "hej@holmdigital.se",
|
|
535
|
+
phoneNumber: metadata?.phoneNumber || "070-123 45 67",
|
|
536
|
+
responseTime: metadata?.responseTime || (lang === "sv" ? "2 dagar" : "2 days"),
|
|
537
|
+
nonComplianceItems,
|
|
538
|
+
locale: lang,
|
|
539
|
+
badgeUrl: generateBadgeUrl(result.score) || void 0,
|
|
540
|
+
publishDate: metadata?.publishDate ? new Date(metadata.publishDate) : void 0
|
|
541
|
+
};
|
|
542
|
+
let content = "";
|
|
543
|
+
if (format === "html") {
|
|
544
|
+
const element = React.createElement(AccessibilityStatement, props);
|
|
545
|
+
const markup = renderToStaticMarkup(element);
|
|
546
|
+
content = `<!DOCTYPE html>
|
|
547
|
+
<html lang="${lang}">
|
|
548
|
+
<head>
|
|
549
|
+
<meta charset="UTF-8">
|
|
550
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
551
|
+
<title>Accessibility Statement - ${props.organizationName}</title>
|
|
552
|
+
<style>
|
|
553
|
+
body { font-family: system-ui, -apple-system, sans-serif; background: #f8f9fa; padding: 2rem; margin: 0; }
|
|
554
|
+
</style>
|
|
555
|
+
</head>
|
|
556
|
+
<body>
|
|
557
|
+
${markup}
|
|
558
|
+
</body>
|
|
559
|
+
</html>`;
|
|
560
|
+
} else {
|
|
561
|
+
const dateStr = props.lastReviewDate.toISOString().split("T")[0];
|
|
562
|
+
const statusMap = {
|
|
563
|
+
"full": "Full",
|
|
564
|
+
"partial": "Partial",
|
|
565
|
+
"non-compliant": "Non-Compliant"
|
|
566
|
+
};
|
|
567
|
+
content = `# Accessibility Statement for ${props.organizationName}
|
|
568
|
+
|
|
569
|
+
This accessibility statement applies to [${props.websiteUrl}](${props.websiteUrl}).
|
|
570
|
+
|
|
571
|
+
## Compliance Status
|
|
572
|
+
|
|
573
|
+
**Status:** ${statusMap[complianceLevel] || complianceLevel}
|
|
574
|
+
|
|
575
|
+
This website is ${complianceLevel} compliant with ${sector === "public" ? "EN 301 549 (WAD)" : "EAA"}.
|
|
576
|
+
|
|
577
|
+
## Non-accessible Content
|
|
578
|
+
|
|
579
|
+
The following content is non-accessible for the following reasons:
|
|
580
|
+
|
|
581
|
+
${nonComplianceItems.length > 0 ? nonComplianceItems.map((item) => `- ${item}`).join("\n") : "_No known issues._"}
|
|
582
|
+
|
|
583
|
+
## Preparation of this statement
|
|
584
|
+
|
|
585
|
+
This statement was prepared on ${dateStr}.
|
|
586
|
+
Method used: **Automated Scan** via @holmdigital/engine.
|
|
587
|
+
|
|
588
|
+
## Feedback and contact information
|
|
589
|
+
|
|
590
|
+
If you need information on this website in a different format like accessible PDF, large print, easy-to-read, audio recording or braille:
|
|
591
|
+
|
|
592
|
+
- email: [${props.contactEmail}](mailto:${props.contactEmail})
|
|
593
|
+
|
|
594
|
+
## Enforcement procedure
|
|
595
|
+
|
|
596
|
+
The enforcement body for ${country} is responsible for enforcing these regulations.
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
return content;
|
|
600
|
+
}
|
|
601
|
+
async function generateStatement(result, outputPath, lang = "en", format = "html", metadata) {
|
|
602
|
+
const content = await generateStatementContent(result, lang, format, metadata);
|
|
603
|
+
const dir = path.dirname(outputPath);
|
|
604
|
+
await fs.mkdir(dir, { recursive: true });
|
|
605
|
+
await fs.writeFile(outputPath, content, "utf-8");
|
|
606
|
+
}
|
|
607
|
+
|
|
439
608
|
export {
|
|
440
609
|
VirtualDOMBuilder,
|
|
441
610
|
RegulatoryScanner,
|
|
442
|
-
PseudoAutomationEngine
|
|
611
|
+
PseudoAutomationEngine,
|
|
612
|
+
generateBadgeUrl,
|
|
613
|
+
generateBadgeMarkdown,
|
|
614
|
+
generateStatementContent,
|
|
615
|
+
generateStatement
|
|
443
616
|
};
|