@zmdb/validator 1.0.0-beta.1

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.
@@ -0,0 +1,53 @@
1
+ export interface ValidationIssue {
2
+ readonly path: string;
3
+ readonly message: string;
4
+ readonly expected?: string;
5
+ readonly value?: unknown;
6
+ }
7
+
8
+ export class ValidationError extends Error {
9
+ readonly issues: readonly ValidationIssue[];
10
+
11
+ constructor(message: string, issues: readonly ValidationIssue[] = []) {
12
+ super(message);
13
+ this.name = 'ValidationError';
14
+ this.issues = issues;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Whether a thrown value claims to be about validation.
20
+ *
21
+ * Structural rather than `instanceof ValidationError`, because a validator a caller wrote
22
+ * themselves throws its own error type — zod's, io-ts's, or one of their own — and the HTTP
23
+ * adapters that ask this question have no business caring which. Carrying an `issues`
24
+ * property is the claim; {@link validationIssuesOf} decides whether it holds up.
25
+ */
26
+ export function claimsValidationIssues(error: unknown): boolean {
27
+ return error !== null && typeof error === 'object' && 'issues' in error;
28
+ }
29
+
30
+ /**
31
+ * The issues on a thrown error, or `undefined` if it carries none worth reporting.
32
+ *
33
+ * Every entry is checked rather than asserted. These end up in a 400 body that a client
34
+ * reads, and "it has an `issues` property" is no evidence that the property holds issues —
35
+ * an error whose `issues` was a string used to be serialized into the response as though it
36
+ * were the list. An entry missing a `path` or a `message` is dropped rather than passed on
37
+ * half-formed; a `ValidationError` with an empty list still answers with the empty list,
38
+ * which is what tells a caller "validation, and it declined to say more".
39
+ */
40
+ export function validationIssuesOf(error: unknown): readonly ValidationIssue[] | undefined {
41
+ if (error === null || typeof error !== 'object' || !('issues' in error)) return undefined;
42
+ const issues: unknown = error.issues;
43
+ if (!Array.isArray(issues)) return undefined;
44
+ return issues.filter(
45
+ (issue: unknown): issue is ValidationIssue =>
46
+ issue !== null &&
47
+ typeof issue === 'object' &&
48
+ 'path' in issue &&
49
+ typeof issue.path === 'string' &&
50
+ 'message' in issue &&
51
+ typeof issue.message === 'string',
52
+ );
53
+ }