forked from getsentry/sentry-javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzoderrors.ts
More file actions
119 lines (104 loc) · 3.22 KB
/
Copy pathzoderrors.ts
File metadata and controls
119 lines (104 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { defineIntegration } from '../integration';
import type { Event, EventHint, IntegrationFn } from '../types-hoist';
import { isError } from '../utils-hoist/is';
import { truncate } from '../utils-hoist/string';
interface ZodErrorsOptions {
key?: string;
limit?: number;
}
const DEFAULT_LIMIT = 10;
const INTEGRATION_NAME = 'ZodErrors';
// Simplified ZodIssue type definition
interface ZodIssue {
path: (string | number)[];
message?: string;
expected?: string | number;
received?: string | number;
unionErrors?: unknown[];
keys?: unknown[];
}
interface ZodError extends Error {
issues: ZodIssue[];
get errors(): ZodError['issues'];
}
function originalExceptionIsZodError(originalException: unknown): originalException is ZodError {
return (
isError(originalException) &&
originalException.name === 'ZodError' &&
Array.isArray((originalException as ZodError).errors)
);
}
type SingleLevelZodIssue<T extends ZodIssue> = {
[P in keyof T]: T[P] extends string | number | undefined
? T[P]
: T[P] extends unknown[]
? string | undefined
: unknown;
};
/**
* Formats child objects or arrays to a string
* That is preserved when sent to Sentry
*/
function formatIssueTitle(issue: ZodIssue): SingleLevelZodIssue<ZodIssue> {
return {
...issue,
path: 'path' in issue && Array.isArray(issue.path) ? issue.path.join('.') : undefined,
keys: 'keys' in issue ? JSON.stringify(issue.keys) : undefined,
unionErrors: 'unionErrors' in issue ? JSON.stringify(issue.unionErrors) : undefined,
};
}
/**
* Zod error message is a stringified version of ZodError.issues
* This doesn't display well in the Sentry UI. Replace it with something shorter.
*/
function formatIssueMessage(zodError: ZodError): string {
const errorKeyMap = new Set<string | number | symbol>();
for (const iss of zodError.issues) {
if (iss.path?.[0]) {
errorKeyMap.add(iss.path[0]);
}
}
const errorKeys = Array.from(errorKeyMap);
return `Failed to validate keys: ${truncate(errorKeys.join(', '), 100)}`;
}
/**
* Applies ZodError issues to an event extras and replaces the error message
*/
export function applyZodErrorsToEvent(limit: number, event: Event, hint?: EventHint): Event {
if (
!event.exception?.values ||
!hint?.originalException ||
!originalExceptionIsZodError(hint.originalException) ||
hint.originalException.issues.length === 0
) {
return event;
}
return {
...event,
exception: {
...event.exception,
values: [
{
...event.exception.values[0],
value: formatIssueMessage(hint.originalException),
},
...event.exception.values.slice(1),
],
},
extra: {
...event.extra,
'zoderror.issues': hint.originalException.errors.slice(0, limit).map(formatIssueTitle),
},
};
}
const _zodErrorsIntegration = ((options: ZodErrorsOptions = {}) => {
const limit = options.limit || DEFAULT_LIMIT;
return {
name: INTEGRATION_NAME,
processEvent(originalEvent, hint) {
const processedEvent = applyZodErrorsToEvent(limit, originalEvent, hint);
return processedEvent;
},
};
}) satisfies IntegrationFn;
export const zodErrorsIntegration = defineIntegration(_zodErrorsIntegration);