@holmdigital/engine 1.2.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/dist/index.js ADDED
@@ -0,0 +1,718 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/locales/en.json
34
+ var en_default;
35
+ var init_en = __esm({
36
+ "src/locales/en.json"() {
37
+ en_default = {
38
+ cli: {
39
+ title: "\n\u{1F310} HolmDigital Regulatory Scanner\n",
40
+ scanning: "Scanning {url}...\n",
41
+ initializing: "Initializing scanner...",
42
+ analyzing: "Analyzing DOM & Shadow DOM...",
43
+ complete: "Scan complete!",
44
+ generating_pdf: "Generating PDF Report...",
45
+ pdf_saved: "PDF Report saved to {path}",
46
+ scan_failed: "Scan failed",
47
+ critical_failure: "\nCI/CD Failure: Critical regulatory violations found.",
48
+ score: "\nCompliance Score: {score}/100",
49
+ status: "Compliance Status: {status}",
50
+ not_compliant: " (Not compliant with legal requirements)",
51
+ viewport: "Viewport: {width}x{height}",
52
+ prescriptive_fix: "\n\u{1F4A1} Prescriptive Fix:",
53
+ use_component: "Use component: {component}",
54
+ pseudo_tests: "\n\u{1F9EC} Generating Pseudo-Automation Tests...\n",
55
+ test_for: "Test for {ruleId}:"
56
+ },
57
+ report: {
58
+ title: "Accessibility Report - {url}",
59
+ generated: "Generated: {date}",
60
+ scan_target: "Scan Target: {url}",
61
+ overall_score: "Overall Score",
62
+ critical_issues: "Critical Issues",
63
+ high_issues: "High Issues",
64
+ total_issues: "Total Issues",
65
+ detailed_violations: "Detailed Violations",
66
+ prescriptive_fix: "\u{1F4A1} Prescriptive Fix",
67
+ use: "Use",
68
+ footer: "Generated by @holmdigital/engine v0.1.0 \u2022 Standards: WCAG 2.1 AA, EN 301 549, DOS-lagen"
69
+ }
70
+ };
71
+ }
72
+ });
73
+
74
+ // src/locales/sv.json
75
+ var sv_default;
76
+ var init_sv = __esm({
77
+ "src/locales/sv.json"() {
78
+ sv_default = {
79
+ cli: {
80
+ title: "\n\u{1F310} HolmDigital Regulatorisk Scanner\n",
81
+ scanning: "Skannar {url}...\n",
82
+ initializing: "Initierar skanner...",
83
+ analyzing: "Analyserar DOM & Shadow DOM...",
84
+ complete: "Skanning klar!",
85
+ generating_pdf: "Genererar PDF-rapport...",
86
+ pdf_saved: "PDF-rapport sparad till {path}",
87
+ scan_failed: "Skanning misslyckades",
88
+ critical_failure: "\nCI/CD Fel: Kritiska regulatoriska brister hittades.",
89
+ score: "\nEfterlevnadspo\xE4ng: {score}/100",
90
+ status: "Efterlevnadsstatus: {status}",
91
+ not_compliant: " (Uppfyller ej legala krav)",
92
+ viewport: "Sk\xE4rmstorlek: {width}x{height}",
93
+ prescriptive_fix: "\n\u{1F4A1} Preskriptiv L\xF6sning:",
94
+ use_component: "Anv\xE4nd komponent: {component}",
95
+ pseudo_tests: "\n\u{1F9EC} Genererar Pseudo-automationstester...\n",
96
+ test_for: "Test f\xF6r {ruleId}:"
97
+ },
98
+ report: {
99
+ title: "Tillg\xE4nglighetsrapport - {url}",
100
+ generated: "Genererad: {date}",
101
+ scan_target: "Skannat m\xE5l: {url}",
102
+ overall_score: "Totalpo\xE4ng",
103
+ critical_issues: "Kritiska fel",
104
+ high_issues: "Allvarliga fel",
105
+ total_issues: "Antal brister",
106
+ detailed_violations: "Detaljerade brister",
107
+ prescriptive_fix: "\u{1F4A1} Preskriptiv L\xF6sning",
108
+ use: "Anv\xE4nd",
109
+ footer: "Genererad av @holmdigital/engine v0.1.0 \u2022 Standarder: WCAG 2.1 AA, EN 301 549, DOS-lagen"
110
+ }
111
+ };
112
+ }
113
+ });
114
+
115
+ // src/locales/de.json
116
+ var de_default;
117
+ var init_de = __esm({
118
+ "src/locales/de.json"() {
119
+ de_default = {
120
+ cli: {
121
+ title: "\n\u{1F310} HolmDigital Regulatorischer Scanner\n",
122
+ scanning: "Scanne {url}...\n",
123
+ initializing: "Initialisiere Scanner...",
124
+ analyzing: "Analysiere DOM & Shadow DOM...",
125
+ complete: "Scan abgeschlossen!",
126
+ generating_pdf: "Generiere PDF-Bericht...",
127
+ pdf_saved: "PDF-Bericht gespeichert unter {path}",
128
+ scan_failed: "Scan fehlgeschlagen",
129
+ critical_failure: "\nCI/CD Fehler: Kritische regulatorische Verst\xF6\xDFe gefunden.",
130
+ score: "\nKonformit\xE4tsbewertung: {score}/100",
131
+ status: "Konformit\xE4tsstatus: {status}",
132
+ not_compliant: " (Nicht konform mit gesetzlichen Anforderungen - BITV 2.0)",
133
+ viewport: "Ansichtsfenster: {width}x{height}",
134
+ prescriptive_fix: "\n\u{1F4A1} Vorschriftsm\xE4\xDFige L\xF6sung:",
135
+ use_component: "Verwenden Sie die Komponente: {component}",
136
+ pseudo_tests: "\n\u{1F9EC} Generiere Pseudo-Automatisierungstests...\n",
137
+ test_for: "Test f\xFCr {ruleId}:"
138
+ },
139
+ report: {
140
+ title: "Barrierefreiheitsbericht - {url}",
141
+ generated: "Generiert: {date}",
142
+ scan_target: "Scan-Ziel: {url}",
143
+ overall_score: "Gesamtbewertung",
144
+ critical_issues: "Kritische Probleme",
145
+ high_issues: "Hohe Probleme",
146
+ total_issues: "Gesamtprobleme",
147
+ detailed_violations: "Detaillierte Verst\xF6\xDFe",
148
+ prescriptive_fix: "\u{1F4A1} Vorschriftsm\xE4\xDFige L\xF6sung",
149
+ use: "Verwenden",
150
+ footer: "Generiert von @holmdigital/engine v0.1.0 \u2022 Standards: WCAG 2.1 AA, EN 301 549, BITV 2.0"
151
+ }
152
+ };
153
+ }
154
+ });
155
+
156
+ // src/locales/fr.json
157
+ var fr_default;
158
+ var init_fr = __esm({
159
+ "src/locales/fr.json"() {
160
+ fr_default = {
161
+ cli: {
162
+ title: "\n\u{1F310} HolmDigital Scanner R\xE9glementaire\n",
163
+ scanning: "Scan de {url} en cours...\n",
164
+ initializing: "Initialisation du scanner...",
165
+ analyzing: "Analyse du DOM & Shadow DOM...",
166
+ complete: "Scan termin\xE9 !",
167
+ generating_pdf: "G\xE9n\xE9ration du rapport PDF...",
168
+ pdf_saved: "Rapport PDF enregistr\xE9 sous {path}",
169
+ scan_failed: "\xC9chec du scan",
170
+ critical_failure: "\n\xC9chec CI/CD : Violations r\xE9glementaires critiques d\xE9tect\xE9es.",
171
+ score: "\nScore de Conformit\xE9 : {score}/100",
172
+ status: "Statut de Conformit\xE9 : {status}",
173
+ not_compliant: " (Non conforme aux exigences l\xE9gales - RGAA)",
174
+ viewport: "Fen\xEAtre d'affichage : {width}x{height}",
175
+ prescriptive_fix: "\n\u{1F4A1} Solution Prescriptive :",
176
+ use_component: "Utilisez le composant : {component}",
177
+ pseudo_tests: "\n\u{1F9EC} G\xE9n\xE9ration de tests de pseudo-automatisation...\n",
178
+ test_for: "Test pour {ruleId} :"
179
+ },
180
+ report: {
181
+ title: "Rapport d'Accessibilit\xE9 - {url}",
182
+ generated: "G\xE9n\xE9r\xE9 le : {date}",
183
+ scan_target: "Cible du scan : {url}",
184
+ overall_score: "Score Global",
185
+ critical_issues: "Probl\xE8mes Critiques",
186
+ high_issues: "Probl\xE8mes Importants",
187
+ total_issues: "Total des Probl\xE8mes",
188
+ detailed_violations: "Violations D\xE9taill\xE9es",
189
+ prescriptive_fix: "\u{1F4A1} Solution Prescriptive",
190
+ use: "Utiliser",
191
+ footer: "G\xE9n\xE9r\xE9 par @holmdigital/engine v0.1.0 \u2022 Standards : WCAG 2.1 AA, EN 301 549, RGAA"
192
+ }
193
+ };
194
+ }
195
+ });
196
+
197
+ // src/locales/es.json
198
+ var es_default;
199
+ var init_es = __esm({
200
+ "src/locales/es.json"() {
201
+ es_default = {
202
+ cli: {
203
+ title: "\n\u{1F310} HolmDigital Esc\xE1ner Regulatorio\n",
204
+ scanning: "Escaneando {url}...\n",
205
+ initializing: "Inicializando esc\xE1ner...",
206
+ analyzing: "Analizando DOM y Shadow DOM...",
207
+ complete: "\xA1Escaneo completo!",
208
+ generating_pdf: "Generando Informe PDF...",
209
+ pdf_saved: "Informe PDF guardado en {path}",
210
+ scan_failed: "Escaneo fallido",
211
+ critical_failure: "\nFallo de CI/CD: Se encontraron violaciones regulatorias cr\xEDticas.",
212
+ score: "\nPuntuaci\xF3n de Cumplimiento: {score}/100",
213
+ status: "Estado de Cumplimiento: {status}",
214
+ not_compliant: " (No cumple con los requisitos legales - UNE 139803)",
215
+ viewport: "Ventana gr\xE1fica: {width}x{height}",
216
+ prescriptive_fix: "\n\u{1F4A1} Soluci\xF3n Prescriptiva:",
217
+ use_component: "Use el componente: {component}",
218
+ pseudo_tests: "\n\u{1F9EC} Generando pruebas de pseudo-automatizaci\xF3n...\n",
219
+ test_for: "Prueba para {ruleId}:"
220
+ },
221
+ report: {
222
+ title: "Informe de Accesibilidad - {url}",
223
+ generated: "Generado: {date}",
224
+ scan_target: "Objetivo de escaneo: {url}",
225
+ overall_score: "Puntuaci\xF3n General",
226
+ critical_issues: "Problemas Cr\xEDticos",
227
+ high_issues: "Problemas Altos",
228
+ total_issues: "Problemas Totales",
229
+ detailed_violations: "Violaciones Detalladas",
230
+ prescriptive_fix: "\u{1F4A1} Soluci\xF3n Prescriptiva",
231
+ use: "Usar",
232
+ footer: "Generado por @holmdigital/engine v0.1.0 \u2022 Est\xE1ndares: WCAG 2.1 AA, EN 301 549, UNE 139803"
233
+ }
234
+ };
235
+ }
236
+ });
237
+
238
+ // src/i18n/index.ts
239
+ var i18n_exports = {};
240
+ __export(i18n_exports, {
241
+ getCurrentLang: () => getCurrentLang,
242
+ setLanguage: () => setLanguage,
243
+ t: () => t
244
+ });
245
+ function setLanguage(lang) {
246
+ if (locales[lang]) {
247
+ currentLang = lang;
248
+ } else {
249
+ console.warn(`Language '${lang}' not found, falling back to 'en'.`);
250
+ currentLang = "en";
251
+ }
252
+ }
253
+ function t(key, params) {
254
+ const keys = key.split(".");
255
+ let value = locales[currentLang];
256
+ for (const k of keys) {
257
+ if (value && typeof value === "object" && k in value) {
258
+ value = value[k];
259
+ } else {
260
+ let fallbackValue = locales["en"];
261
+ for (const fk of keys) {
262
+ if (fallbackValue && typeof fallbackValue === "object" && fk in fallbackValue) {
263
+ fallbackValue = fallbackValue[fk];
264
+ } else {
265
+ return key;
266
+ }
267
+ }
268
+ value = fallbackValue;
269
+ break;
270
+ }
271
+ }
272
+ if (typeof value !== "string") return key;
273
+ if (params) {
274
+ return value.replace(/{(\w+)}/g, (_, k) => {
275
+ return params[k] !== void 0 ? String(params[k]) : `{${k}}`;
276
+ });
277
+ }
278
+ return value;
279
+ }
280
+ function getCurrentLang() {
281
+ return currentLang;
282
+ }
283
+ var locales, currentLang;
284
+ var init_i18n = __esm({
285
+ "src/i18n/index.ts"() {
286
+ "use strict";
287
+ init_en();
288
+ init_sv();
289
+ init_de();
290
+ init_fr();
291
+ init_es();
292
+ locales = {
293
+ en: en_default,
294
+ sv: sv_default,
295
+ de: de_default,
296
+ fr: fr_default,
297
+ es: es_default
298
+ };
299
+ currentLang = "en";
300
+ }
301
+ });
302
+
303
+ // src/index.ts
304
+ var index_exports = {};
305
+ __export(index_exports, {
306
+ PseudoAutomationEngine: () => PseudoAutomationEngine,
307
+ RegulatoryScanner: () => RegulatoryScanner,
308
+ VirtualDOMBuilder: () => VirtualDOMBuilder
309
+ });
310
+ module.exports = __toCommonJS(index_exports);
311
+
312
+ // src/core/regulatory-scanner.ts
313
+ var import_puppeteer = __toESM(require("puppeteer"));
314
+
315
+ // src/core/virtual-dom.ts
316
+ var VirtualDOMBuilder = class {
317
+ page;
318
+ constructor(page) {
319
+ this.page = page;
320
+ }
321
+ /**
322
+ * Bygg ett virtuellt träd av hela sidan
323
+ */
324
+ async build(config = {}) {
325
+ return await this.page.evaluate((config2) => {
326
+ let nodeIdCounter = 0;
327
+ function generateId() {
328
+ return `vn-${++nodeIdCounter}`;
329
+ }
330
+ function getAttributes(element) {
331
+ const attrs = {};
332
+ for (let i = 0; i < element.attributes.length; i++) {
333
+ const attr = element.attributes[i];
334
+ attrs[attr.name] = attr.value;
335
+ }
336
+ return attrs;
337
+ }
338
+ function getRect(element) {
339
+ const r = element.getBoundingClientRect();
340
+ return {
341
+ x: r.x,
342
+ y: r.y,
343
+ width: r.width,
344
+ height: r.height
345
+ };
346
+ }
347
+ function traverse(node, parent, depth = 0) {
348
+ if (!node) return null;
349
+ if (config2.maxDepth && depth > config2.maxDepth) return null;
350
+ if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) return null;
351
+ const element = node;
352
+ const isShadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
353
+ const tagName = isShadowRoot ? "#shadow-root" : element.tagName.toLowerCase();
354
+ const attributes = !isShadowRoot ? getAttributes(element) : {};
355
+ let computedStyle;
356
+ if (!isShadowRoot && config2.includeComputedStyle) {
357
+ const style = window.getComputedStyle(element);
358
+ computedStyle = {};
359
+ config2.includeComputedStyle.forEach((prop) => {
360
+ computedStyle[prop] = style.getPropertyValue(prop);
361
+ });
362
+ }
363
+ const vNode = {
364
+ nodeId: generateId(),
365
+ tagName,
366
+ attributes,
367
+ children: [],
368
+ parentId: parent?.nodeId,
369
+ isShadowRoot,
370
+ rect: !isShadowRoot ? getRect(element) : { x: 0, y: 0, width: 0, height: 0 },
371
+ computedStyle,
372
+ textContent: node.textContent || void 0
373
+ };
374
+ if (isShadowRoot && node.mode) {
375
+ vNode.shadowMode = node.mode;
376
+ }
377
+ if (element.childNodes) {
378
+ Array.from(element.childNodes).forEach((child) => {
379
+ if (child.nodeType === Node.ELEMENT_NODE) {
380
+ const childVNode = traverse(child, vNode, depth + 1);
381
+ if (childVNode) vNode.children.push(childVNode);
382
+ }
383
+ });
384
+ }
385
+ if (!isShadowRoot && element.shadowRoot) {
386
+ const shadowVNode = traverse(element.shadowRoot, vNode, depth + 1);
387
+ if (shadowVNode) vNode.children.push(shadowVNode);
388
+ }
389
+ return vNode;
390
+ }
391
+ if (!document.body) {
392
+ return {
393
+ nodeId: "root-fallback",
394
+ tagName: "body",
395
+ attributes: {},
396
+ children: [],
397
+ rect: { x: 0, y: 0, width: 0, height: 0 }
398
+ };
399
+ }
400
+ return traverse(document.body);
401
+ }, config);
402
+ }
403
+ };
404
+
405
+ // src/core/html-validator.ts
406
+ var import_html_validate = require("html-validate");
407
+ var HtmlValidator = class {
408
+ validator;
409
+ constructor() {
410
+ const config = {
411
+ extends: ["html-validate:recommended"],
412
+ rules: {
413
+ "no-deprecated-attr": "off",
414
+ // Focus on structure, not deprecation
415
+ "prefer-native-element": "off",
416
+ "no-trailing-whitespace": "off",
417
+ "void-style": "off",
418
+ "no-inline-style": "off",
419
+ // Allowed for A11y overrides
420
+ "no-implicit-button-type": "off"
421
+ // Common in React
422
+ }
423
+ };
424
+ this.validator = new import_html_validate.HtmlValidate(config);
425
+ }
426
+ async validate(html) {
427
+ const report = await this.validator.validateString(html);
428
+ return {
429
+ valid: report.valid,
430
+ errors: report.results.flatMap(
431
+ (result) => result.messages.map((msg) => ({
432
+ rule: msg.ruleId,
433
+ message: msg.message,
434
+ line: msg.line,
435
+ column: msg.column,
436
+ selector: msg.selector ?? void 0
437
+ }))
438
+ )
439
+ };
440
+ }
441
+ };
442
+
443
+ // src/core/regulatory-scanner.ts
444
+ var RegulatoryScanner = class {
445
+ browser = null;
446
+ options;
447
+ htmlValidator;
448
+ constructor(options) {
449
+ this.options = {
450
+ headless: true,
451
+ standard: "dos-lagen",
452
+ // Default till striktaste
453
+ silent: false,
454
+ ...options
455
+ };
456
+ this.htmlValidator = new HtmlValidator();
457
+ }
458
+ /** Log only when not in silent mode */
459
+ log(message) {
460
+ if (!this.options.silent) {
461
+ console.log(message);
462
+ }
463
+ }
464
+ /**
465
+ * Kör en fullständig regulatorisk scan
466
+ */
467
+ async scan() {
468
+ try {
469
+ await this.initBrowser();
470
+ const page = await this.getPage();
471
+ if (this.options.viewport) {
472
+ await page.setViewport(this.options.viewport);
473
+ }
474
+ let retries = 3;
475
+ while (retries > 0) {
476
+ try {
477
+ await page.goto(this.options.url, {
478
+ waitUntil: "domcontentloaded",
479
+ timeout: 6e4
480
+ });
481
+ break;
482
+ } catch (e) {
483
+ retries--;
484
+ if (retries === 0) throw e;
485
+ this.log(`Navigation failed, retrying... (${retries} attempts left)`);
486
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
487
+ }
488
+ }
489
+ try {
490
+ await page.waitForNetworkIdle({
491
+ idleTime: 500,
492
+ timeout: 1e4,
493
+ concurrency: 2
494
+ });
495
+ } catch (e) {
496
+ this.log("Network busy, proceeding with scan anyway...");
497
+ }
498
+ const pageContent = await page.content();
499
+ const htmlValidation = await this.htmlValidator.validate(pageContent);
500
+ if (!htmlValidation.valid) {
501
+ this.log(`HTML Validation: Found ${htmlValidation.errors.length} structural issues.`);
502
+ }
503
+ const vDomBuilder = new VirtualDOMBuilder(page);
504
+ await vDomBuilder.build({ includeComputedStyle: ["color", "background-color"] });
505
+ await this.injectAxe(page);
506
+ this.log("Axe injected. Running analysis...");
507
+ const axeResults = await page.evaluate(async () => {
508
+ if (!document || !document.documentElement) {
509
+ return { violations: [] };
510
+ }
511
+ return await window.axe.run(document, {
512
+ iframes: false
513
+ // Inaktivera iframe-scanning för att undvika kraschar på tunga annons-sajter
514
+ // Vi tar bort runOnly tillfälligt för att se ALLA fel
515
+ /*
516
+ runOnly: {
517
+ type: 'tag',
518
+ values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
519
+ }
520
+ */
521
+ });
522
+ });
523
+ this.log(`Raw Axe Violations: ${axeResults.violations?.length || 0}`);
524
+ const regulatoryReports = await this.enrichResults(axeResults);
525
+ const result = this.generateResultPackage(regulatoryReports);
526
+ result.htmlValidation = htmlValidation;
527
+ return result;
528
+ } finally {
529
+ await this.close();
530
+ }
531
+ }
532
+ async initBrowser() {
533
+ this.browser = await import_puppeteer.default.launch({
534
+ headless: this.options.headless,
535
+ args: [
536
+ "--no-sandbox",
537
+ "--disable-setuid-sandbox",
538
+ "--disable-blink-features=AutomationControlled"
539
+ // Gömmer att det är en robot
540
+ ]
541
+ });
542
+ }
543
+ async getPage() {
544
+ if (!this.browser) throw new Error("Browser not initialized");
545
+ const page = await this.browser.newPage();
546
+ await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
547
+ return page;
548
+ }
549
+ async injectAxe(page) {
550
+ const axeSource = require("axe-core").source;
551
+ await page.evaluate(axeSource);
552
+ }
553
+ async enrichResults(axeResults) {
554
+ const reports = [];
555
+ const { searchRulesByTags, generateRegulatoryReport } = await import("@holmdigital/standards");
556
+ const { getCurrentLang: getCurrentLang2 } = await Promise.resolve().then(() => (init_i18n(), i18n_exports));
557
+ const lang = getCurrentLang2();
558
+ for (const violation of axeResults.violations) {
559
+ let report = generateRegulatoryReport(violation.id, lang);
560
+ if (!report) {
561
+ const matchingRules = searchRulesByTags(violation.tags, lang);
562
+ if (matchingRules.length > 0) {
563
+ report = generateRegulatoryReport(matchingRules[0].ruleId, lang);
564
+ }
565
+ }
566
+ if (report) {
567
+ reports.push({
568
+ ...report,
569
+ holmdigitalInsight: {
570
+ ...report.holmdigitalInsight,
571
+ reasoning: violation.help
572
+ // Använd Axe's hjälptext som specifik anledning
573
+ },
574
+ // Attach extra debug info for the CLI
575
+ failingNodes: violation.nodes.map((node) => ({
576
+ html: node.html,
577
+ target: node.target.join(" "),
578
+ failureSummary: node.failureSummary
579
+ }))
580
+ });
581
+ } else {
582
+ reports.push({
583
+ ruleId: violation.id,
584
+ wcagCriteria: "Unknown",
585
+ en301549Criteria: "Unknown",
586
+ dosLagenReference: "Kr\xE4ver manuell bed\xF6mning",
587
+ diggRisk: "medium",
588
+ // Default risk
589
+ eaaImpact: "medium",
590
+ remediation: {
591
+ description: violation.help,
592
+ technicalGuidance: violation.description,
593
+ component: void 0
594
+ },
595
+ holmdigitalInsight: {
596
+ diggRisk: "medium",
597
+ eaaImpact: "medium",
598
+ swedishInterpretation: violation.help,
599
+ priorityRationale: "Detta fel uppt\xE4cktes av scannern men saknar specifik mappning i HolmDigital-databasen."
600
+ },
601
+ testability: {
602
+ automated: true,
603
+ requiresManualCheck: false,
604
+ pseudoAutomation: false,
605
+ complexity: "moderate"
606
+ }
607
+ });
608
+ }
609
+ }
610
+ return reports;
611
+ }
612
+ generateResultPackage(reports) {
613
+ const stats = {
614
+ critical: reports.filter((r) => r.holmdigitalInsight.diggRisk === "critical").length,
615
+ high: reports.filter((r) => r.holmdigitalInsight.diggRisk === "high").length,
616
+ medium: reports.filter((r) => r.holmdigitalInsight.diggRisk === "medium").length,
617
+ low: reports.filter((r) => r.holmdigitalInsight.diggRisk === "low").length,
618
+ total: reports.length
619
+ };
620
+ const weightedScore = stats.critical * 25 + // Critical violations are severe
621
+ stats.high * 15 + // High risk affects usability significantly
622
+ stats.medium * 5 + // Medium annoyance
623
+ stats.low * 1;
624
+ const score = Math.max(0, 100 - weightedScore);
625
+ const complianceStatus = stats.total === 0 ? "PASS" : "FAIL";
626
+ return {
627
+ url: this.options.url,
628
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
629
+ reports,
630
+ stats,
631
+ score,
632
+ complianceStatus
633
+ };
634
+ }
635
+ /**
636
+ * Stänger webbläsaren och frigör resurser
637
+ */
638
+ async close() {
639
+ if (this.browser) {
640
+ await this.browser.close();
641
+ this.browser = null;
642
+ }
643
+ }
644
+ };
645
+
646
+ // src/automation/pseudo-automation.ts
647
+ var PseudoAutomationEngine = class {
648
+ /**
649
+ * Generera ett Playwright-testskript baserat på en rapport
650
+ */
651
+ generateTestScript(report, url) {
652
+ if (!report.holmdigitalInsight.diggRisk) return "";
653
+ const testName = `Verify ${report.wcagCriteria} - ${report.ruleId}`;
654
+ return `
655
+ import { test, expect } from '@playwright/test';
656
+
657
+ /**
658
+ * GENERATED PSEUDO-AUTOMATION TEST
659
+ * Rule: ${report.ruleId}
660
+ * WCAG: ${report.wcagCriteria}
661
+ * EN 301 549: ${report.en301549Criteria}
662
+ * Risk: ${report.holmdigitalInsight.diggRisk}
663
+ *
664
+ * Manual Verification Required:
665
+ * ${report.remediation.description}
666
+ */
667
+
668
+ test('${testName}', async ({ page }) => {
669
+ // 1. Navigate to target
670
+ await page.goto('${url}');
671
+
672
+ // 2. Initial state verification
673
+ // TODO: Add specific selectors based on report details
674
+
675
+ // 3. Interaction steps (Example for Keyboard Navigation)
676
+ if ('${report.ruleId}' === 'keyboard-accessible') {
677
+ console.log('Verifying tab order...');
678
+ await page.keyboard.press('Tab');
679
+
680
+ // Assert focus state
681
+ const focused = await page.evaluate(() => document.activeElement?.tagName);
682
+ console.log('Focused element:', focused);
683
+
684
+ // Add assertions here based on expected focus order
685
+ }
686
+
687
+ // 4. Verification
688
+ // Manually verify that...
689
+ });
690
+ `;
691
+ }
692
+ /**
693
+ * Generera en checklista för manuell testning i Markdown
694
+ */
695
+ generateManualChecklist(report) {
696
+ return `
697
+ ### \u{1F575}\uFE0F Manual Verification: ${report.ruleId}
698
+
699
+ **Regulatory Context**
700
+ - **WCAG**: ${report.wcagCriteria}
701
+ - **DOS-lagen**: ${report.dosLagenReference}
702
+ - **Risk**: ${report.holmdigitalInsight.diggRisk.toUpperCase()}
703
+
704
+ **Instructions**
705
+ 1. [ ] ${report.remediation.description}
706
+ 2. [ ] Verify against technical guidance: ${report.remediation.technicalGuidance}
707
+
708
+ **HolmDigital Insight**
709
+ > ${report.holmdigitalInsight.swedishInterpretation}
710
+ `;
711
+ }
712
+ };
713
+ // Annotate the CommonJS export names for ESM import in node:
714
+ 0 && (module.exports = {
715
+ PseudoAutomationEngine,
716
+ RegulatoryScanner,
717
+ VirtualDOMBuilder
718
+ });