@codewithagents/api-errors 0.1.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/README.md +113 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +163 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +161 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @codewithagents/api-errors
|
|
2
|
+
|
|
3
|
+
Backend API errors don't automatically map to form field errors. Every project writes the same glue code from scratch: catch the error, inspect its shape, figure out which field each message belongs to, then call your form library's error setter. This package does that for you — framework-agnostic core with a first-class React Hook Form adapter.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @codewithagents/api-errors
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### With React Hook Form
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useForm } from 'react-hook-form'
|
|
17
|
+
import { mapApiErrors } from '@codewithagents/api-errors'
|
|
18
|
+
|
|
19
|
+
function SignupForm() {
|
|
20
|
+
const { register, handleSubmit, setError, formState: { errors } } = useForm()
|
|
21
|
+
|
|
22
|
+
const onSubmit = async (data) => {
|
|
23
|
+
try {
|
|
24
|
+
await api.post('/signup', data)
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Automatically maps backend field errors to RHF setError calls
|
|
27
|
+
mapApiErrors(error, setError)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
33
|
+
<input {...register('email')} />
|
|
34
|
+
{errors.email && <p>{errors.email.message}</p>}
|
|
35
|
+
|
|
36
|
+
<input {...register('name')} />
|
|
37
|
+
{errors.name && <p>{errors.name.message}</p>}
|
|
38
|
+
|
|
39
|
+
<button type="submit">Sign up</button>
|
|
40
|
+
</form>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Standalone with `extractFieldErrors`
|
|
46
|
+
|
|
47
|
+
Use `extractFieldErrors` when you need the normalized error list without a form library:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { extractFieldErrors } from '@codewithagents/api-errors'
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await submitData(payload)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const fieldErrors = extractFieldErrors(error)
|
|
56
|
+
// [{ field: 'email', message: 'must not be blank' }, ...]
|
|
57
|
+
|
|
58
|
+
for (const { field, message } of fieldErrors) {
|
|
59
|
+
console.warn(`${field}: ${message}`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
You can customize the fallback field name and transform field names:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const fieldErrors = extractFieldErrors(error, {
|
|
68
|
+
fallbackField: 'root', // used when no field is found (default: 'root')
|
|
69
|
+
transformField: (f) => f.replace(/([A-Z])/g, '.$1').toLowerCase(), // camelCase → dot.path
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Supported Error Formats
|
|
74
|
+
|
|
75
|
+
**RFC 7807 Problem Details** (Spring Boot 3+, standard):
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"type": "...",
|
|
79
|
+
"title": "Validation failed",
|
|
80
|
+
"status": 400,
|
|
81
|
+
"errors": {
|
|
82
|
+
"email": ["must not be blank"],
|
|
83
|
+
"name": ["too short"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Spring Boot default validation format** (older):
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"status": 400,
|
|
92
|
+
"errors": [
|
|
93
|
+
{ "field": "email", "defaultMessage": "must not be blank" }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Simple flat format** (custom APIs):
|
|
99
|
+
```json
|
|
100
|
+
{ "field": "email", "message": "Invalid email" }
|
|
101
|
+
```
|
|
102
|
+
or as an array:
|
|
103
|
+
```json
|
|
104
|
+
[{ "field": "email", "message": "Invalid email" }]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## TypeScript
|
|
108
|
+
|
|
109
|
+
This package is written in TypeScript and ships full type declarations. No `@types` package needed.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import type { FieldError, MapApiErrorsOptions } from '@codewithagents/api-errors'
|
|
113
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized representation of a single field-level error.
|
|
3
|
+
*/
|
|
4
|
+
export interface FieldError {
|
|
5
|
+
field: string;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Options for mapApiErrors and extractFieldErrors.
|
|
10
|
+
*/
|
|
11
|
+
export interface MapApiErrorsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Fallback field name when no field can be determined from the error.
|
|
14
|
+
* Defaults to 'root'.
|
|
15
|
+
*/
|
|
16
|
+
fallbackField?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional transform applied to every field name before it is returned.
|
|
19
|
+
* Useful for mapping backend camelCase field names to nested React Hook Form
|
|
20
|
+
* paths, e.g. `"addressCity"` → `"address.city"`.
|
|
21
|
+
*/
|
|
22
|
+
transformField?: (field: string) => string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract normalized field errors from an unknown API error response.
|
|
26
|
+
*
|
|
27
|
+
* Supports:
|
|
28
|
+
* - RFC 7807 Problem Details (Spring Boot 3+)
|
|
29
|
+
* - Spring Boot default validation format
|
|
30
|
+
* - Simple flat `{ field, message }` object
|
|
31
|
+
* - Array of `{ field, message }` objects
|
|
32
|
+
*
|
|
33
|
+
* Never throws — returns an empty array for unrecognized shapes.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractFieldErrors(error: unknown, options?: MapApiErrorsOptions): FieldError[];
|
|
36
|
+
/**
|
|
37
|
+
* React Hook Form adapter.
|
|
38
|
+
*
|
|
39
|
+
* Extracts field errors from an unknown API error response and calls
|
|
40
|
+
* React Hook Form's `setError` for each one.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* try {
|
|
45
|
+
* await submitForm(data)
|
|
46
|
+
* } catch (error) {
|
|
47
|
+
* mapApiErrors(error, setError)
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function mapApiErrors(error: unknown, setError: (field: string, error: {
|
|
52
|
+
type: string;
|
|
53
|
+
message: string;
|
|
54
|
+
}) => void, options?: MapApiErrorsOptions): void;
|
|
55
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CAC3C;AAmID;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,UAAU,EAAE,CAyCd;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,EAC3E,OAAO,CAAC,EAAE,mBAAmB,GAC5B,IAAI,CAKN"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Internal type guards
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
function isObject(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function isStringArray(value) {
|
|
8
|
+
return Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
9
|
+
}
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Format detectors + parsers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* RFC 7807 Problem Details (Spring Boot 3+ default):
|
|
15
|
+
* { "errors": { "email": ["must not be blank"], "name": ["too short"] } }
|
|
16
|
+
*/
|
|
17
|
+
function tryParseRfc7807(body, fallbackField, transformField) {
|
|
18
|
+
const { errors } = body;
|
|
19
|
+
if (!isObject(errors))
|
|
20
|
+
return null;
|
|
21
|
+
const result = [];
|
|
22
|
+
for (const [rawField, messages] of Object.entries(errors)) {
|
|
23
|
+
const field = transformField(rawField);
|
|
24
|
+
if (isStringArray(messages)) {
|
|
25
|
+
for (const message of messages) {
|
|
26
|
+
result.push({ field, message });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (typeof messages === 'string') {
|
|
30
|
+
result.push({ field, message: messages });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Unknown value shape — use fallback field with stringified message
|
|
34
|
+
result.push({ field: transformField(fallbackField), message: String(messages) });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result.length > 0 ? result : null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Spring Boot default validation format (pre-3):
|
|
41
|
+
* { "errors": [{ "field": "email", "defaultMessage": "must not be blank" }] }
|
|
42
|
+
*/
|
|
43
|
+
function tryParseSpringArray(body, fallbackField, transformField) {
|
|
44
|
+
const { errors } = body;
|
|
45
|
+
if (!Array.isArray(errors))
|
|
46
|
+
return null;
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const item of errors) {
|
|
49
|
+
if (!isObject(item))
|
|
50
|
+
continue;
|
|
51
|
+
const rawField = typeof item['field'] === 'string' ? item['field'] : fallbackField;
|
|
52
|
+
const message = typeof item['defaultMessage'] === 'string'
|
|
53
|
+
? item['defaultMessage']
|
|
54
|
+
: typeof item['message'] === 'string'
|
|
55
|
+
? item['message']
|
|
56
|
+
: 'Unknown error';
|
|
57
|
+
result.push({ field: transformField(rawField), message });
|
|
58
|
+
}
|
|
59
|
+
return result.length > 0 ? result : null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Simple flat object format:
|
|
63
|
+
* { "field": "email", "message": "Invalid email" }
|
|
64
|
+
*/
|
|
65
|
+
function tryParseFlatObject(body, fallbackField, transformField) {
|
|
66
|
+
const rawField = typeof body['field'] === 'string' ? body['field'] : null;
|
|
67
|
+
const message = typeof body['message'] === 'string' ? body['message'] : null;
|
|
68
|
+
if (message === null)
|
|
69
|
+
return null;
|
|
70
|
+
const field = rawField !== null ? rawField : fallbackField;
|
|
71
|
+
return [{ field: transformField(field), message }];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Array of simple flat objects:
|
|
75
|
+
* [{ "field": "email", "message": "Invalid email" }]
|
|
76
|
+
*/
|
|
77
|
+
function tryParseFlatArray(body, fallbackField, transformField) {
|
|
78
|
+
const result = [];
|
|
79
|
+
for (const item of body) {
|
|
80
|
+
if (!isObject(item))
|
|
81
|
+
continue;
|
|
82
|
+
const rawField = typeof item['field'] === 'string' ? item['field'] : fallbackField;
|
|
83
|
+
const message = typeof item['message'] === 'string' ? item['message'] : null;
|
|
84
|
+
if (message === null)
|
|
85
|
+
continue;
|
|
86
|
+
result.push({ field: transformField(rawField), message });
|
|
87
|
+
}
|
|
88
|
+
return result.length > 0 ? result : null;
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Public API
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
/**
|
|
94
|
+
* Extract normalized field errors from an unknown API error response.
|
|
95
|
+
*
|
|
96
|
+
* Supports:
|
|
97
|
+
* - RFC 7807 Problem Details (Spring Boot 3+)
|
|
98
|
+
* - Spring Boot default validation format
|
|
99
|
+
* - Simple flat `{ field, message }` object
|
|
100
|
+
* - Array of `{ field, message }` objects
|
|
101
|
+
*
|
|
102
|
+
* Never throws — returns an empty array for unrecognized shapes.
|
|
103
|
+
*/
|
|
104
|
+
export function extractFieldErrors(error, options) {
|
|
105
|
+
const fallbackField = options?.fallbackField ?? 'root';
|
|
106
|
+
const transformField = options?.transformField ?? ((f) => f);
|
|
107
|
+
try {
|
|
108
|
+
// Unwrap common error wrapper shapes (e.g. Axios response, fetch response body)
|
|
109
|
+
let body = error;
|
|
110
|
+
// Support objects with a `response.data` shape (Axios-style)
|
|
111
|
+
if (isObject(body) && isObject(body['response']) && body['response']['data'] !== undefined) {
|
|
112
|
+
body = body['response']['data'];
|
|
113
|
+
}
|
|
114
|
+
// Support objects with a `data` property that contains the actual error body
|
|
115
|
+
if (isObject(body) && isObject(body['data']) && !('field' in body) && !('errors' in body)) {
|
|
116
|
+
body = body['data'];
|
|
117
|
+
}
|
|
118
|
+
// Array of flat error objects
|
|
119
|
+
if (Array.isArray(body)) {
|
|
120
|
+
return tryParseFlatArray(body, fallbackField, transformField) ?? [];
|
|
121
|
+
}
|
|
122
|
+
if (!isObject(body))
|
|
123
|
+
return [];
|
|
124
|
+
// Try RFC 7807 / Spring Boot 3 format first (object-style errors map)
|
|
125
|
+
const rfc7807 = tryParseRfc7807(body, fallbackField, transformField);
|
|
126
|
+
if (rfc7807 !== null)
|
|
127
|
+
return rfc7807;
|
|
128
|
+
// Try Spring Boot default format (array-style errors list)
|
|
129
|
+
const springArray = tryParseSpringArray(body, fallbackField, transformField);
|
|
130
|
+
if (springArray !== null)
|
|
131
|
+
return springArray;
|
|
132
|
+
// Try simple flat object
|
|
133
|
+
const flat = tryParseFlatObject(body, fallbackField, transformField);
|
|
134
|
+
if (flat !== null)
|
|
135
|
+
return flat;
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* React Hook Form adapter.
|
|
144
|
+
*
|
|
145
|
+
* Extracts field errors from an unknown API error response and calls
|
|
146
|
+
* React Hook Form's `setError` for each one.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* try {
|
|
151
|
+
* await submitForm(data)
|
|
152
|
+
* } catch (error) {
|
|
153
|
+
* mapApiErrors(error, setError)
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function mapApiErrors(error, setError, options) {
|
|
158
|
+
const fieldErrors = extractFieldErrors(error, options);
|
|
159
|
+
for (const { field, message } of fieldErrors) {
|
|
160
|
+
setError(field, { type: 'server', message });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { extractFieldErrors, mapApiErrors } from './index.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// extractFieldErrors
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
describe('extractFieldErrors', () => {
|
|
7
|
+
describe('RFC 7807 / Spring Boot 3+ format', () => {
|
|
8
|
+
it('maps a single field with one message', () => {
|
|
9
|
+
const error = { errors: { email: ['must not be blank'] } };
|
|
10
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
11
|
+
{ field: 'email', message: 'must not be blank' },
|
|
12
|
+
]);
|
|
13
|
+
});
|
|
14
|
+
it('maps multiple fields', () => {
|
|
15
|
+
const error = {
|
|
16
|
+
errors: {
|
|
17
|
+
email: ['must not be blank'],
|
|
18
|
+
name: ['too short'],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const result = extractFieldErrors(error);
|
|
22
|
+
expect(result).toHaveLength(2);
|
|
23
|
+
expect(result).toContainEqual({ field: 'email', message: 'must not be blank' });
|
|
24
|
+
expect(result).toContainEqual({ field: 'name', message: 'too short' });
|
|
25
|
+
});
|
|
26
|
+
it('expands multiple messages per field into separate FieldErrors', () => {
|
|
27
|
+
const error = { errors: { email: ['must not be blank', 'invalid format'] } };
|
|
28
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
29
|
+
{ field: 'email', message: 'must not be blank' },
|
|
30
|
+
{ field: 'email', message: 'invalid format' },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('Spring Boot default validation format (array)', () => {
|
|
35
|
+
it('maps field + defaultMessage', () => {
|
|
36
|
+
const error = {
|
|
37
|
+
status: 400,
|
|
38
|
+
errors: [{ field: 'email', defaultMessage: 'must not be blank' }],
|
|
39
|
+
};
|
|
40
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
41
|
+
{ field: 'email', message: 'must not be blank' },
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
it('falls back to message when defaultMessage is absent', () => {
|
|
45
|
+
const error = {
|
|
46
|
+
errors: [{ field: 'email', message: 'must not be blank' }],
|
|
47
|
+
};
|
|
48
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
49
|
+
{ field: 'email', message: 'must not be blank' },
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
it('maps multiple entries', () => {
|
|
53
|
+
const error = {
|
|
54
|
+
errors: [
|
|
55
|
+
{ field: 'email', defaultMessage: 'must not be blank' },
|
|
56
|
+
{ field: 'name', defaultMessage: 'too short' },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
expect(extractFieldErrors(error)).toHaveLength(2);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('flat object format', () => {
|
|
63
|
+
it('maps { field, message } directly', () => {
|
|
64
|
+
const error = { field: 'email', message: 'Invalid email' };
|
|
65
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
66
|
+
{ field: 'email', message: 'Invalid email' },
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
it('uses fallbackField when field is absent', () => {
|
|
70
|
+
const error = { message: 'Something went wrong' };
|
|
71
|
+
expect(extractFieldErrors(error, { fallbackField: 'root' })).toEqual([
|
|
72
|
+
{ field: 'root', message: 'Something went wrong' },
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('flat array format', () => {
|
|
77
|
+
it('maps an array of { field, message } objects', () => {
|
|
78
|
+
const error = [
|
|
79
|
+
{ field: 'email', message: 'Invalid' },
|
|
80
|
+
{ field: 'name', message: 'Required' },
|
|
81
|
+
];
|
|
82
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
83
|
+
{ field: 'email', message: 'Invalid' },
|
|
84
|
+
{ field: 'name', message: 'Required' },
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
it('skips entries without a message', () => {
|
|
88
|
+
const error = [{ field: 'email' }, { field: 'name', message: 'Required' }];
|
|
89
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
90
|
+
{ field: 'name', message: 'Required' },
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('Axios-style response wrapper', () => {
|
|
95
|
+
it('unwraps response.data before parsing', () => {
|
|
96
|
+
const error = {
|
|
97
|
+
response: {
|
|
98
|
+
data: { errors: { email: ['must not be blank'] } },
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
expect(extractFieldErrors(error)).toEqual([
|
|
102
|
+
{ field: 'email', message: 'must not be blank' },
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('unrecognized / unknown shapes', () => {
|
|
107
|
+
it('returns [] for an unrecognized object', () => {
|
|
108
|
+
expect(extractFieldErrors({ foo: 'bar' })).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
it('returns [] for null', () => {
|
|
111
|
+
expect(extractFieldErrors(null)).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
it('returns [] for undefined', () => {
|
|
114
|
+
expect(extractFieldErrors(undefined)).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
it('returns [] for a plain string', () => {
|
|
117
|
+
expect(extractFieldErrors('something went wrong')).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
it('never throws', () => {
|
|
120
|
+
expect(() => extractFieldErrors({ deeply: { nested: { evil: true } } })).not.toThrow();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe('options', () => {
|
|
124
|
+
it('uses fallbackField for errors without a field name', () => {
|
|
125
|
+
const error = { message: 'Unauthorised' };
|
|
126
|
+
const result = extractFieldErrors(error, { fallbackField: 'serverError' });
|
|
127
|
+
expect(result).toEqual([{ field: 'serverError', message: 'Unauthorised' }]);
|
|
128
|
+
});
|
|
129
|
+
it('applies transformField to every resolved field name', () => {
|
|
130
|
+
const error = { errors: { emailAddress: ['invalid'] } };
|
|
131
|
+
const result = extractFieldErrors(error, {
|
|
132
|
+
transformField: (f) => f.replace(/([A-Z])/g, '.$1').toLowerCase(),
|
|
133
|
+
});
|
|
134
|
+
expect(result).toEqual([{ field: 'email.address', message: 'invalid' }]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// mapApiErrors
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
describe('mapApiErrors', () => {
|
|
142
|
+
it('calls setError for each field error with type "server"', () => {
|
|
143
|
+
const setError = vi.fn();
|
|
144
|
+
const error = { errors: { email: ['must not be blank'], name: ['too short'] } };
|
|
145
|
+
mapApiErrors(error, setError);
|
|
146
|
+
expect(setError).toHaveBeenCalledTimes(2);
|
|
147
|
+
expect(setError).toHaveBeenCalledWith('email', { type: 'server', message: 'must not be blank' });
|
|
148
|
+
expect(setError).toHaveBeenCalledWith('name', { type: 'server', message: 'too short' });
|
|
149
|
+
});
|
|
150
|
+
it('never calls setError for unrecognized errors', () => {
|
|
151
|
+
const setError = vi.fn();
|
|
152
|
+
mapApiErrors({ foo: 'bar' }, setError);
|
|
153
|
+
expect(setError).not.toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
it('never calls setError for null', () => {
|
|
156
|
+
const setError = vi.fn();
|
|
157
|
+
mapApiErrors(null, setError);
|
|
158
|
+
expect(setError).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codewithagents/api-errors",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Map backend API errors to form field errors. Framework-agnostic core with React Hook Form adapter.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"api",
|
|
19
|
+
"errors",
|
|
20
|
+
"form",
|
|
21
|
+
"react-hook-form",
|
|
22
|
+
"validation",
|
|
23
|
+
"typescript"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.8.0",
|
|
28
|
+
"vitest": "^3.0.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"lint": "tsc --noEmit"
|
|
34
|
+
}
|
|
35
|
+
}
|