Suite of AT Protocol TypeScript libraries built on web standards
1import { failureError, type ResultFailure } from "../core/result.ts";
2import { arrayAgg } from "../util/array-agg.ts";
3import {
4 type Issue,
5 IssueInvalidType,
6 IssueInvalidValue,
7} from "./validation-issue.ts";
8
9export class ValidationError extends Error {
10 override name = "ValidationError";
11
12 readonly issues: Issue[];
13
14 constructor(issues: Issue[], options?: ErrorOptions) {
15 const issuesAgg = aggregateIssues(issues);
16 super(issuesAgg.join(", "), options);
17 this.issues = issuesAgg;
18 }
19
20 static fromFailures(
21 failures: ResultFailure<ValidationError>[],
22 ): ValidationError {
23 if (failures.length === 1) return failures[0].error;
24 const issues = failures.flatMap(extractFailureIssues);
25 return new ValidationError(issues, {
26 cause: failures.map(failureError),
27 });
28 }
29}
30
31function extractFailureIssues(result: ResultFailure<ValidationError>) {
32 return result.error.issues;
33}
34
35function aggregateIssues(issues: Issue[]): Issue[] {
36 if (issues.length <= 1) return issues;
37 if (issues.length === 2 && issues[0].code !== issues[1].code) return issues;
38
39 return [
40 ...arrayAgg(
41 issues.filter((issue) => issue instanceof IssueInvalidType),
42 (a, b) => comparePropertyPaths(a.path, b.path),
43 (issues) =>
44 new IssueInvalidType(
45 issues[0].path,
46 issues[0].input,
47 Array.from(new Set(issues.flatMap((iss) => iss.expected))),
48 ),
49 ),
50 ...arrayAgg(
51 issues.filter((issue) => issue instanceof IssueInvalidValue),
52 (a, b) => comparePropertyPaths(a.path, b.path),
53 (issues) =>
54 new IssueInvalidValue(
55 issues[0].path,
56 issues[0].input,
57 Array.from(new Set(issues.flatMap((iss) => iss.values))),
58 ),
59 ),
60 ...issues.filter(
61 (issue) =>
62 !(issue instanceof IssueInvalidType) &&
63 !(issue instanceof IssueInvalidValue),
64 ),
65 ];
66}
67
68function comparePropertyPaths(
69 a: readonly PropertyKey[],
70 b: readonly PropertyKey[],
71) {
72 if (a.length !== b.length) return false;
73 for (let i = 0; i < a.length; i++) {
74 if (a[i] !== b[i]) return false;
75 }
76 return true;
77}