@holmdigital/engine 1.4.5 → 1.4.6
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/README.md +52 -12
- package/dist/{chunk-VTSEAYGK.mjs → chunk-SH2EU7XX.mjs} +38 -4
- package/dist/cli/index.js +41 -6
- package/dist/cli/index.mjs +4 -3
- package/dist/index.d.mts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +38 -4
- package/dist/index.mjs +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ It handles the heavy lifting of:
|
|
|
21
21
|
- **Regulatory Mapping**: Maps technical failures to EU laws (EN 301 549, EAA).
|
|
22
22
|
- **HTML Structure Validation**: Built-in `html-validate` checks to prevent false positives/negatives.
|
|
23
23
|
- **Internationalization (i18n)**: Supports English (`en`), Swedish (`sv`), German (`de`), French (`fr`), Spanish (`es`), and Dutch (`nl`).
|
|
24
|
+
- **Configurable Severity Threshold**: Fail CI only on critical/high issues (configurable).
|
|
25
|
+
- **Rich Metadata**: Includes scan duration, page title, language, and version info.
|
|
24
26
|
- **Pseudo-Automation**: Automatically generates Playwright/Puppeteer test scripts for manual verification steps.
|
|
25
27
|
- **PDF Reporting**: Generates beautiful, compliant PDF reports out of the box.
|
|
26
28
|
- **TypeScript**: Written in TypeScript with full type definitions included.
|
|
@@ -38,33 +40,71 @@ npx hd-a11y-scan <url> [options]
|
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
**Options:**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
| Option | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `--lang <code>` | Language code (`en`, `sv`, `de`, `fr`, `es`, `nl`, `en-us`, `en-gb`) |
|
|
46
|
+
| `--threshold <level>` | Severity threshold for compliance (`critical`, `high`, `medium`, `low`). Default: `high` |
|
|
47
|
+
| `--ci` | Run in CI mode (exit code 1 on failure) |
|
|
48
|
+
| `--json` | Output results as JSON |
|
|
49
|
+
| `--pdf <path>` | Generate a PDF report |
|
|
50
|
+
| `--viewport <size>` | Set viewport size (`mobile`, `tablet`, `desktop`, or custom `1024x768`) |
|
|
51
|
+
| `--generate-tests` | Generate Pseudo-Automation tests |
|
|
52
|
+
| `--api-key <key>` | API Key for HolmDigital Cloud |
|
|
53
|
+
| `--cloud-url <url>` | Custom URL for HolmDigital Cloud API |
|
|
54
|
+
|
|
55
|
+
**Example:**
|
|
56
|
+
```bash
|
|
57
|
+
# Fail only on critical issues in CI
|
|
58
|
+
npx hd-a11y-scan https://example.com --ci --threshold critical
|
|
59
|
+
|
|
60
|
+
# Full JSON output with metadata
|
|
61
|
+
npx hd-a11y-scan https://example.com --json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## JSON Output
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"url": "https://example.com",
|
|
69
|
+
"timestamp": "2026-01-13T17:05:11.749Z",
|
|
70
|
+
"metadata": {
|
|
71
|
+
"engineVersion": "1.4.6",
|
|
72
|
+
"axeCoreVersion": "4.10.2",
|
|
73
|
+
"standardsVersion": "1.2.2",
|
|
74
|
+
"scanDuration": 2891,
|
|
75
|
+
"pageTitle": "Example Domain",
|
|
76
|
+
"pageLanguage": "en"
|
|
77
|
+
},
|
|
78
|
+
"stats": {
|
|
79
|
+
"passed": 13,
|
|
80
|
+
"critical": 0,
|
|
81
|
+
"high": 0,
|
|
82
|
+
"medium": 2,
|
|
83
|
+
"low": 0,
|
|
84
|
+
"total": 2
|
|
85
|
+
},
|
|
86
|
+
"score": 90,
|
|
87
|
+
"complianceStatus": "PASS"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
49
90
|
|
|
50
91
|
## Programmatic Usage
|
|
51
92
|
|
|
52
93
|
```typescript
|
|
53
94
|
import { RegulatoryScanner, setLanguage } from '@holmdigital/engine';
|
|
54
95
|
|
|
55
|
-
// Initialize Scanner
|
|
56
96
|
const scanner = new RegulatoryScanner({
|
|
57
97
|
url: 'https://example.com',
|
|
58
|
-
|
|
98
|
+
severityThreshold: 'high' // critical, high, medium, low
|
|
59
99
|
});
|
|
60
100
|
|
|
61
|
-
// Set Language context (optional, defaults to 'en')
|
|
62
101
|
setLanguage('sv');
|
|
63
102
|
|
|
64
|
-
// Run Scan
|
|
65
103
|
const result = await scanner.scan();
|
|
66
104
|
|
|
67
105
|
console.log(`Score: ${result.score}`);
|
|
106
|
+
console.log(`Duration: ${result.metadata.scanDuration}ms`);
|
|
107
|
+
console.log(`Passed: ${result.stats.passed}, Failed: ${result.stats.total}`);
|
|
68
108
|
```
|
|
69
109
|
|
|
70
110
|
## License
|
|
@@ -158,6 +158,10 @@ var RegulatoryScanner = class {
|
|
|
158
158
|
* Kör en fullständig regulatorisk scan
|
|
159
159
|
*/
|
|
160
160
|
async scan() {
|
|
161
|
+
const startTime = Date.now();
|
|
162
|
+
let pageTitle;
|
|
163
|
+
let pageLanguage;
|
|
164
|
+
let passedCount = 0;
|
|
161
165
|
try {
|
|
162
166
|
await this.initBrowser();
|
|
163
167
|
const page = await this.getPage();
|
|
@@ -188,6 +192,8 @@ var RegulatoryScanner = class {
|
|
|
188
192
|
} catch (e) {
|
|
189
193
|
this.log("Network busy, proceeding with scan anyway...");
|
|
190
194
|
}
|
|
195
|
+
pageTitle = await page.title();
|
|
196
|
+
pageLanguage = await page.evaluate(() => document.documentElement.lang || void 0);
|
|
191
197
|
const pageContent = await page.content();
|
|
192
198
|
const htmlValidation = await this.htmlValidator.validate(pageContent);
|
|
193
199
|
if (!htmlValidation.valid) {
|
|
@@ -199,7 +205,7 @@ var RegulatoryScanner = class {
|
|
|
199
205
|
this.log("Axe injected. Running analysis...");
|
|
200
206
|
const axeResults = await page.evaluate(async () => {
|
|
201
207
|
if (!document || !document.documentElement) {
|
|
202
|
-
return { violations: [] };
|
|
208
|
+
return { violations: [], passes: [] };
|
|
203
209
|
}
|
|
204
210
|
return await window.axe.run(document, {
|
|
205
211
|
iframes: false
|
|
@@ -214,8 +220,10 @@ var RegulatoryScanner = class {
|
|
|
214
220
|
});
|
|
215
221
|
});
|
|
216
222
|
this.log(`Raw Axe Violations: ${axeResults.violations?.length || 0}`);
|
|
223
|
+
passedCount = axeResults.passes?.length || 0;
|
|
217
224
|
const regulatoryReports = await this.enrichResults(axeResults);
|
|
218
|
-
const
|
|
225
|
+
const scanDuration = Date.now() - startTime;
|
|
226
|
+
const result = this.generateResultPackage(regulatoryReports, passedCount, scanDuration, pageTitle, pageLanguage);
|
|
219
227
|
result.htmlValidation = htmlValidation;
|
|
220
228
|
return result;
|
|
221
229
|
} finally {
|
|
@@ -302,8 +310,9 @@ var RegulatoryScanner = class {
|
|
|
302
310
|
}
|
|
303
311
|
return reports;
|
|
304
312
|
}
|
|
305
|
-
generateResultPackage(reports) {
|
|
313
|
+
generateResultPackage(reports, passedCount, scanDuration, pageTitle, pageLanguage) {
|
|
306
314
|
const stats = {
|
|
315
|
+
passed: passedCount,
|
|
307
316
|
critical: reports.filter((r) => r.holmdigitalInsight.diggRisk === "critical").length,
|
|
308
317
|
high: reports.filter((r) => r.holmdigitalInsight.diggRisk === "high").length,
|
|
309
318
|
medium: reports.filter((r) => r.holmdigitalInsight.diggRisk === "medium").length,
|
|
@@ -315,10 +324,35 @@ var RegulatoryScanner = class {
|
|
|
315
324
|
stats.medium * 5 + // Medium annoyance
|
|
316
325
|
stats.low * 1;
|
|
317
326
|
const score = Math.max(0, 100 - weightedScore);
|
|
318
|
-
const
|
|
327
|
+
const threshold = this.options.severityThreshold || "high";
|
|
328
|
+
let complianceStatus = "PASS";
|
|
329
|
+
switch (threshold) {
|
|
330
|
+
case "critical":
|
|
331
|
+
complianceStatus = stats.critical > 0 ? "FAIL" : "PASS";
|
|
332
|
+
break;
|
|
333
|
+
case "high":
|
|
334
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 ? "FAIL" : "PASS";
|
|
335
|
+
break;
|
|
336
|
+
case "medium":
|
|
337
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 || stats.medium > 0 ? "FAIL" : "PASS";
|
|
338
|
+
break;
|
|
339
|
+
case "low":
|
|
340
|
+
complianceStatus = stats.total > 0 ? "FAIL" : "PASS";
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
const axeCore = __require("axe-core");
|
|
344
|
+
const metadata = {
|
|
345
|
+
engineVersion: "1.4.6",
|
|
346
|
+
axeCoreVersion: axeCore.version || "4.10.2",
|
|
347
|
+
standardsVersion: "1.2.2",
|
|
348
|
+
scanDuration,
|
|
349
|
+
pageTitle,
|
|
350
|
+
pageLanguage
|
|
351
|
+
};
|
|
319
352
|
return {
|
|
320
353
|
url: this.options.url,
|
|
321
354
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
355
|
+
metadata,
|
|
322
356
|
reports,
|
|
323
357
|
stats,
|
|
324
358
|
score,
|
package/dist/cli/index.js
CHANGED
|
@@ -507,6 +507,10 @@ var RegulatoryScanner = class {
|
|
|
507
507
|
* Kör en fullständig regulatorisk scan
|
|
508
508
|
*/
|
|
509
509
|
async scan() {
|
|
510
|
+
const startTime = Date.now();
|
|
511
|
+
let pageTitle;
|
|
512
|
+
let pageLanguage;
|
|
513
|
+
let passedCount = 0;
|
|
510
514
|
try {
|
|
511
515
|
await this.initBrowser();
|
|
512
516
|
const page = await this.getPage();
|
|
@@ -537,6 +541,8 @@ var RegulatoryScanner = class {
|
|
|
537
541
|
} catch (e) {
|
|
538
542
|
this.log("Network busy, proceeding with scan anyway...");
|
|
539
543
|
}
|
|
544
|
+
pageTitle = await page.title();
|
|
545
|
+
pageLanguage = await page.evaluate(() => document.documentElement.lang || void 0);
|
|
540
546
|
const pageContent = await page.content();
|
|
541
547
|
const htmlValidation = await this.htmlValidator.validate(pageContent);
|
|
542
548
|
if (!htmlValidation.valid) {
|
|
@@ -548,7 +554,7 @@ var RegulatoryScanner = class {
|
|
|
548
554
|
this.log("Axe injected. Running analysis...");
|
|
549
555
|
const axeResults = await page.evaluate(async () => {
|
|
550
556
|
if (!document || !document.documentElement) {
|
|
551
|
-
return { violations: [] };
|
|
557
|
+
return { violations: [], passes: [] };
|
|
552
558
|
}
|
|
553
559
|
return await window.axe.run(document, {
|
|
554
560
|
iframes: false
|
|
@@ -563,8 +569,10 @@ var RegulatoryScanner = class {
|
|
|
563
569
|
});
|
|
564
570
|
});
|
|
565
571
|
this.log(`Raw Axe Violations: ${axeResults.violations?.length || 0}`);
|
|
572
|
+
passedCount = axeResults.passes?.length || 0;
|
|
566
573
|
const regulatoryReports = await this.enrichResults(axeResults);
|
|
567
|
-
const
|
|
574
|
+
const scanDuration = Date.now() - startTime;
|
|
575
|
+
const result = this.generateResultPackage(regulatoryReports, passedCount, scanDuration, pageTitle, pageLanguage);
|
|
568
576
|
result.htmlValidation = htmlValidation;
|
|
569
577
|
return result;
|
|
570
578
|
} finally {
|
|
@@ -651,8 +659,9 @@ var RegulatoryScanner = class {
|
|
|
651
659
|
}
|
|
652
660
|
return reports;
|
|
653
661
|
}
|
|
654
|
-
generateResultPackage(reports) {
|
|
662
|
+
generateResultPackage(reports, passedCount, scanDuration, pageTitle, pageLanguage) {
|
|
655
663
|
const stats = {
|
|
664
|
+
passed: passedCount,
|
|
656
665
|
critical: reports.filter((r) => r.holmdigitalInsight.diggRisk === "critical").length,
|
|
657
666
|
high: reports.filter((r) => r.holmdigitalInsight.diggRisk === "high").length,
|
|
658
667
|
medium: reports.filter((r) => r.holmdigitalInsight.diggRisk === "medium").length,
|
|
@@ -664,10 +673,35 @@ var RegulatoryScanner = class {
|
|
|
664
673
|
stats.medium * 5 + // Medium annoyance
|
|
665
674
|
stats.low * 1;
|
|
666
675
|
const score = Math.max(0, 100 - weightedScore);
|
|
667
|
-
const
|
|
676
|
+
const threshold = this.options.severityThreshold || "high";
|
|
677
|
+
let complianceStatus = "PASS";
|
|
678
|
+
switch (threshold) {
|
|
679
|
+
case "critical":
|
|
680
|
+
complianceStatus = stats.critical > 0 ? "FAIL" : "PASS";
|
|
681
|
+
break;
|
|
682
|
+
case "high":
|
|
683
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 ? "FAIL" : "PASS";
|
|
684
|
+
break;
|
|
685
|
+
case "medium":
|
|
686
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 || stats.medium > 0 ? "FAIL" : "PASS";
|
|
687
|
+
break;
|
|
688
|
+
case "low":
|
|
689
|
+
complianceStatus = stats.total > 0 ? "FAIL" : "PASS";
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
const axeCore = require("axe-core");
|
|
693
|
+
const metadata = {
|
|
694
|
+
engineVersion: "1.4.6",
|
|
695
|
+
axeCoreVersion: axeCore.version || "4.10.2",
|
|
696
|
+
standardsVersion: "1.2.2",
|
|
697
|
+
scanDuration,
|
|
698
|
+
pageTitle,
|
|
699
|
+
pageLanguage
|
|
700
|
+
};
|
|
668
701
|
return {
|
|
669
702
|
url: this.options.url,
|
|
670
703
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
704
|
+
metadata,
|
|
671
705
|
reports,
|
|
672
706
|
stats,
|
|
673
707
|
score,
|
|
@@ -1094,7 +1128,7 @@ function isValidUrl(urlString) {
|
|
|
1094
1128
|
}
|
|
1095
1129
|
var program = new import_commander.Command();
|
|
1096
1130
|
program.name("hd-a11y-scan").description("HolmDigital Regulatory Scanner").version("0.1.0");
|
|
1097
|
-
program.argument("<url>", "URL to scan").option("--lang <code>", "Language code (en, sv)", "en").option("--ci", "Run in CI/CD mode (exit code 1 on critical failures)").option("--generate-tests", "Generate Pseudo-Automation tests").option("--json", "Output as JSON").option("--pdf <path>", "Generate PDF report to path").option("--viewport <size>", 'Set viewport (e.g. "mobile", "desktop", "1024x768")').option("--api-key <key>", "API key for HolmDigital Cloud authentication").option("--cloud-url <url>", "Cloud API URL", "https://cloud.holmdigital.se").action(async (url, options) => {
|
|
1131
|
+
program.argument("<url>", "URL to scan").option("--lang <code>", "Language code (en, sv)", "en").option("--ci", "Run in CI/CD mode (exit code 1 on critical failures)").option("--generate-tests", "Generate Pseudo-Automation tests").option("--json", "Output as JSON").option("--pdf <path>", "Generate PDF report to path").option("--viewport <size>", 'Set viewport (e.g. "mobile", "desktop", "1024x768")').option("--threshold <level>", "Severity threshold for compliance (critical, high, medium, low)", "high").option("--api-key <key>", "API key for HolmDigital Cloud authentication").option("--cloud-url <url>", "Cloud API URL", "https://cloud.holmdigital.se").action(async (url, options) => {
|
|
1098
1132
|
setLanguage(options.lang);
|
|
1099
1133
|
if (!isValidUrl(url)) {
|
|
1100
1134
|
console.error(import_chalk.default.red(`Error: Invalid URL format '${url}'`));
|
|
@@ -1122,8 +1156,9 @@ program.argument("<url>", "URL to scan").option("--lang <code>", "Language code
|
|
|
1122
1156
|
url,
|
|
1123
1157
|
failOnCritical: options.ci,
|
|
1124
1158
|
viewport,
|
|
1125
|
-
silent: options.json
|
|
1159
|
+
silent: options.json,
|
|
1126
1160
|
// Suppress debug output for JSON mode
|
|
1161
|
+
severityThreshold: options.threshold
|
|
1127
1162
|
});
|
|
1128
1163
|
if (spinner) spinner.text = t("cli.analyzing");
|
|
1129
1164
|
const result = await scanner.scan();
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PseudoAutomationEngine,
|
|
4
4
|
RegulatoryScanner
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-SH2EU7XX.mjs";
|
|
6
6
|
import {
|
|
7
7
|
getCurrentLang,
|
|
8
8
|
setLanguage,
|
|
@@ -351,7 +351,7 @@ function isValidUrl(urlString) {
|
|
|
351
351
|
}
|
|
352
352
|
var program = new Command();
|
|
353
353
|
program.name("hd-a11y-scan").description("HolmDigital Regulatory Scanner").version("0.1.0");
|
|
354
|
-
program.argument("<url>", "URL to scan").option("--lang <code>", "Language code (en, sv)", "en").option("--ci", "Run in CI/CD mode (exit code 1 on critical failures)").option("--generate-tests", "Generate Pseudo-Automation tests").option("--json", "Output as JSON").option("--pdf <path>", "Generate PDF report to path").option("--viewport <size>", 'Set viewport (e.g. "mobile", "desktop", "1024x768")').option("--api-key <key>", "API key for HolmDigital Cloud authentication").option("--cloud-url <url>", "Cloud API URL", "https://cloud.holmdigital.se").action(async (url, options) => {
|
|
354
|
+
program.argument("<url>", "URL to scan").option("--lang <code>", "Language code (en, sv)", "en").option("--ci", "Run in CI/CD mode (exit code 1 on critical failures)").option("--generate-tests", "Generate Pseudo-Automation tests").option("--json", "Output as JSON").option("--pdf <path>", "Generate PDF report to path").option("--viewport <size>", 'Set viewport (e.g. "mobile", "desktop", "1024x768")').option("--threshold <level>", "Severity threshold for compliance (critical, high, medium, low)", "high").option("--api-key <key>", "API key for HolmDigital Cloud authentication").option("--cloud-url <url>", "Cloud API URL", "https://cloud.holmdigital.se").action(async (url, options) => {
|
|
355
355
|
setLanguage(options.lang);
|
|
356
356
|
if (!isValidUrl(url)) {
|
|
357
357
|
console.error(chalk.red(`Error: Invalid URL format '${url}'`));
|
|
@@ -379,8 +379,9 @@ program.argument("<url>", "URL to scan").option("--lang <code>", "Language code
|
|
|
379
379
|
url,
|
|
380
380
|
failOnCritical: options.ci,
|
|
381
381
|
viewport,
|
|
382
|
-
silent: options.json
|
|
382
|
+
silent: options.json,
|
|
383
383
|
// Suppress debug output for JSON mode
|
|
384
|
+
severityThreshold: options.threshold
|
|
384
385
|
});
|
|
385
386
|
if (spinner) spinner.text = t("cli.analyzing");
|
|
386
387
|
const result = await scanner.scan();
|
package/dist/index.d.mts
CHANGED
|
@@ -22,12 +22,23 @@ interface ScannerOptions {
|
|
|
22
22
|
height: number;
|
|
23
23
|
};
|
|
24
24
|
silent?: boolean;
|
|
25
|
+
severityThreshold?: 'critical' | 'high' | 'medium' | 'low';
|
|
26
|
+
}
|
|
27
|
+
interface ScanMetadata {
|
|
28
|
+
engineVersion: string;
|
|
29
|
+
axeCoreVersion: string;
|
|
30
|
+
standardsVersion: string;
|
|
31
|
+
scanDuration: number;
|
|
32
|
+
pageTitle?: string;
|
|
33
|
+
pageLanguage?: string;
|
|
25
34
|
}
|
|
26
35
|
interface ScanResult {
|
|
27
36
|
url: string;
|
|
28
37
|
timestamp: string;
|
|
38
|
+
metadata: ScanMetadata;
|
|
29
39
|
reports: RegulatoryReport[];
|
|
30
40
|
stats: {
|
|
41
|
+
passed: number;
|
|
31
42
|
critical: number;
|
|
32
43
|
high: number;
|
|
33
44
|
medium: number;
|
|
@@ -131,4 +142,4 @@ declare function setLanguage(lang: string): void;
|
|
|
131
142
|
declare function t(key: LocaleKey, params?: Record<string, string | number>): string;
|
|
132
143
|
declare function getCurrentLang(): string;
|
|
133
144
|
|
|
134
|
-
export { PseudoAutomationEngine, RegulatoryScanner, type ScanResult, type ScannerOptions, VirtualDOMBuilder, type VirtualDOMConfig, type VirtualNode, getCurrentLang, setLanguage, t };
|
|
145
|
+
export { PseudoAutomationEngine, RegulatoryScanner, type ScanMetadata, type ScanResult, type ScannerOptions, VirtualDOMBuilder, type VirtualDOMConfig, type VirtualNode, getCurrentLang, setLanguage, t };
|
package/dist/index.d.ts
CHANGED
|
@@ -22,12 +22,23 @@ interface ScannerOptions {
|
|
|
22
22
|
height: number;
|
|
23
23
|
};
|
|
24
24
|
silent?: boolean;
|
|
25
|
+
severityThreshold?: 'critical' | 'high' | 'medium' | 'low';
|
|
26
|
+
}
|
|
27
|
+
interface ScanMetadata {
|
|
28
|
+
engineVersion: string;
|
|
29
|
+
axeCoreVersion: string;
|
|
30
|
+
standardsVersion: string;
|
|
31
|
+
scanDuration: number;
|
|
32
|
+
pageTitle?: string;
|
|
33
|
+
pageLanguage?: string;
|
|
25
34
|
}
|
|
26
35
|
interface ScanResult {
|
|
27
36
|
url: string;
|
|
28
37
|
timestamp: string;
|
|
38
|
+
metadata: ScanMetadata;
|
|
29
39
|
reports: RegulatoryReport[];
|
|
30
40
|
stats: {
|
|
41
|
+
passed: number;
|
|
31
42
|
critical: number;
|
|
32
43
|
high: number;
|
|
33
44
|
medium: number;
|
|
@@ -131,4 +142,4 @@ declare function setLanguage(lang: string): void;
|
|
|
131
142
|
declare function t(key: LocaleKey, params?: Record<string, string | number>): string;
|
|
132
143
|
declare function getCurrentLang(): string;
|
|
133
144
|
|
|
134
|
-
export { PseudoAutomationEngine, RegulatoryScanner, type ScanResult, type ScannerOptions, VirtualDOMBuilder, type VirtualDOMConfig, type VirtualNode, getCurrentLang, setLanguage, t };
|
|
145
|
+
export { PseudoAutomationEngine, RegulatoryScanner, type ScanMetadata, type ScanResult, type ScannerOptions, VirtualDOMBuilder, type VirtualDOMConfig, type VirtualNode, getCurrentLang, setLanguage, t };
|
package/dist/index.js
CHANGED
|
@@ -514,6 +514,10 @@ var RegulatoryScanner = class {
|
|
|
514
514
|
* Kör en fullständig regulatorisk scan
|
|
515
515
|
*/
|
|
516
516
|
async scan() {
|
|
517
|
+
const startTime = Date.now();
|
|
518
|
+
let pageTitle;
|
|
519
|
+
let pageLanguage;
|
|
520
|
+
let passedCount = 0;
|
|
517
521
|
try {
|
|
518
522
|
await this.initBrowser();
|
|
519
523
|
const page = await this.getPage();
|
|
@@ -544,6 +548,8 @@ var RegulatoryScanner = class {
|
|
|
544
548
|
} catch (e) {
|
|
545
549
|
this.log("Network busy, proceeding with scan anyway...");
|
|
546
550
|
}
|
|
551
|
+
pageTitle = await page.title();
|
|
552
|
+
pageLanguage = await page.evaluate(() => document.documentElement.lang || void 0);
|
|
547
553
|
const pageContent = await page.content();
|
|
548
554
|
const htmlValidation = await this.htmlValidator.validate(pageContent);
|
|
549
555
|
if (!htmlValidation.valid) {
|
|
@@ -555,7 +561,7 @@ var RegulatoryScanner = class {
|
|
|
555
561
|
this.log("Axe injected. Running analysis...");
|
|
556
562
|
const axeResults = await page.evaluate(async () => {
|
|
557
563
|
if (!document || !document.documentElement) {
|
|
558
|
-
return { violations: [] };
|
|
564
|
+
return { violations: [], passes: [] };
|
|
559
565
|
}
|
|
560
566
|
return await window.axe.run(document, {
|
|
561
567
|
iframes: false
|
|
@@ -570,8 +576,10 @@ var RegulatoryScanner = class {
|
|
|
570
576
|
});
|
|
571
577
|
});
|
|
572
578
|
this.log(`Raw Axe Violations: ${axeResults.violations?.length || 0}`);
|
|
579
|
+
passedCount = axeResults.passes?.length || 0;
|
|
573
580
|
const regulatoryReports = await this.enrichResults(axeResults);
|
|
574
|
-
const
|
|
581
|
+
const scanDuration = Date.now() - startTime;
|
|
582
|
+
const result = this.generateResultPackage(regulatoryReports, passedCount, scanDuration, pageTitle, pageLanguage);
|
|
575
583
|
result.htmlValidation = htmlValidation;
|
|
576
584
|
return result;
|
|
577
585
|
} finally {
|
|
@@ -658,8 +666,9 @@ var RegulatoryScanner = class {
|
|
|
658
666
|
}
|
|
659
667
|
return reports;
|
|
660
668
|
}
|
|
661
|
-
generateResultPackage(reports) {
|
|
669
|
+
generateResultPackage(reports, passedCount, scanDuration, pageTitle, pageLanguage) {
|
|
662
670
|
const stats = {
|
|
671
|
+
passed: passedCount,
|
|
663
672
|
critical: reports.filter((r) => r.holmdigitalInsight.diggRisk === "critical").length,
|
|
664
673
|
high: reports.filter((r) => r.holmdigitalInsight.diggRisk === "high").length,
|
|
665
674
|
medium: reports.filter((r) => r.holmdigitalInsight.diggRisk === "medium").length,
|
|
@@ -671,10 +680,35 @@ var RegulatoryScanner = class {
|
|
|
671
680
|
stats.medium * 5 + // Medium annoyance
|
|
672
681
|
stats.low * 1;
|
|
673
682
|
const score = Math.max(0, 100 - weightedScore);
|
|
674
|
-
const
|
|
683
|
+
const threshold = this.options.severityThreshold || "high";
|
|
684
|
+
let complianceStatus = "PASS";
|
|
685
|
+
switch (threshold) {
|
|
686
|
+
case "critical":
|
|
687
|
+
complianceStatus = stats.critical > 0 ? "FAIL" : "PASS";
|
|
688
|
+
break;
|
|
689
|
+
case "high":
|
|
690
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 ? "FAIL" : "PASS";
|
|
691
|
+
break;
|
|
692
|
+
case "medium":
|
|
693
|
+
complianceStatus = stats.critical > 0 || stats.high > 0 || stats.medium > 0 ? "FAIL" : "PASS";
|
|
694
|
+
break;
|
|
695
|
+
case "low":
|
|
696
|
+
complianceStatus = stats.total > 0 ? "FAIL" : "PASS";
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
const axeCore = require("axe-core");
|
|
700
|
+
const metadata = {
|
|
701
|
+
engineVersion: "1.4.6",
|
|
702
|
+
axeCoreVersion: axeCore.version || "4.10.2",
|
|
703
|
+
standardsVersion: "1.2.2",
|
|
704
|
+
scanDuration,
|
|
705
|
+
pageTitle,
|
|
706
|
+
pageLanguage
|
|
707
|
+
};
|
|
675
708
|
return {
|
|
676
709
|
url: this.options.url,
|
|
677
710
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
711
|
+
metadata,
|
|
678
712
|
reports,
|
|
679
713
|
stats,
|
|
680
714
|
score,
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holmdigital/engine",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -61,12 +61,12 @@
|
|
|
61
61
|
"license": "MIT",
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@holmdigital/standards": "*",
|
|
64
|
-
"axe-core": "
|
|
64
|
+
"axe-core": "4.10.2",
|
|
65
65
|
"chalk": "^5.3.0",
|
|
66
66
|
"commander": "^12.1.0",
|
|
67
|
-
"html-validate": "
|
|
67
|
+
"html-validate": "10.4.0",
|
|
68
68
|
"ora": "^8.1.1",
|
|
69
|
-
"puppeteer": "
|
|
69
|
+
"puppeteer": "23.10.4",
|
|
70
70
|
"ws": "^8.18.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|