@codewithagents/api-errors 1.1.0 → 1.2.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.
- package/README.md +201 -32
- package/dist/index.d.ts +61 -41
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +81 -420
- package/dist/index.js.map +1 -1
- package/dist/parsers/dispatch.d.ts +12 -0
- package/dist/parsers/dispatch.d.ts.map +1 -0
- package/dist/parsers/dispatch.js +116 -0
- package/dist/parsers/dispatch.js.map +1 -0
- package/dist/parsers/flat.d.ts +12 -0
- package/dist/parsers/flat.d.ts.map +1 -0
- package/dist/parsers/flat.js +31 -0
- package/dist/parsers/flat.js.map +1 -0
- package/dist/parsers/graphql-extensions.d.ts +18 -0
- package/dist/parsers/graphql-extensions.d.ts.map +1 -0
- package/dist/parsers/graphql-extensions.js +81 -0
- package/dist/parsers/graphql-extensions.js.map +1 -0
- package/dist/parsers/invalid-params.d.ts +7 -0
- package/dist/parsers/invalid-params.d.ts.map +1 -0
- package/dist/parsers/invalid-params.js +12 -0
- package/dist/parsers/invalid-params.js.map +1 -0
- package/dist/parsers/json-api.d.ts +14 -0
- package/dist/parsers/json-api.d.ts.map +1 -0
- package/dist/parsers/json-api.js +85 -0
- package/dist/parsers/json-api.js.map +1 -0
- package/dist/parsers/laravel-drf.d.ts +10 -0
- package/dist/parsers/laravel-drf.d.ts.map +1 -0
- package/dist/parsers/laravel-drf.js +46 -0
- package/dist/parsers/laravel-drf.js.map +1 -0
- package/dist/parsers/rfc7807.d.ts +7 -0
- package/dist/parsers/rfc7807.d.ts.map +1 -0
- package/dist/parsers/rfc7807.js +24 -0
- package/dist/parsers/rfc7807.js.map +1 -0
- package/dist/parsers/spring.d.ts +7 -0
- package/dist/parsers/spring.d.ts.map +1 -0
- package/dist/parsers/spring.js +24 -0
- package/dist/parsers/spring.js.map +1 -0
- package/dist/parsers/types.d.ts +44 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +2 -0
- package/dist/parsers/types.js.map +1 -0
- package/dist/parsers/unwrap.d.ts +3 -0
- package/dist/parsers/unwrap.d.ts.map +1 -0
- package/dist/parsers/unwrap.js +29 -0
- package/dist/parsers/unwrap.js.map +1 -0
- package/dist/parsers/utils.d.ts +12 -0
- package/dist/parsers/utils.d.ts.map +1 -0
- package/dist/parsers/utils.js +65 -0
- package/dist/parsers/utils.js.map +1 -0
- package/dist/parsers/violations.d.ts +7 -0
- package/dist/parsers/violations.d.ts.map +1 -0
- package/dist/parsers/violations.js +12 -0
- package/dist/parsers/violations.js.map +1 -0
- package/dist/parsers/zod-flatten.d.ts +11 -0
- package/dist/parsers/zod-flatten.d.ts.map +1 -0
- package/dist/parsers/zod-flatten.js +37 -0
- package/dist/parsers/zod-flatten.js.map +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -5,15 +5,19 @@
|
|
|
5
5
|
[](https://codecov.io/gh/codewithagents/openapi-zod-ts)
|
|
6
6
|
[](https://github.com/codewithagents/openapi-zod-ts/actions/workflows/codeql.yml)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**[Full documentation](https://openapi.codewithagents.de/api-errors)**
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`@codewithagents/api-errors` maps backend API error responses to form field errors in one call: it auto-detects RFC 9457 Problem Details, Spring Boot, Laravel, Zod, JSON:API, and ten other error shapes, then wires the result to React Hook Form, Formik, or TanStack Form with zero runtime dependencies.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
12
|
+
It is the runtime helper in the [openapi-zod-ts toolchain](#ecosystem): the four generators emit your types, client, hooks, and mocks, and `api-errors` maps the error responses those servers return back onto your form fields at runtime.
|
|
13
|
+
|
|
14
|
+
- **Framework-agnostic core.** `extractFieldErrors(error)` returns normalized `{ field, message }` pairs from any supported error shape. `extractErrors(error)` returns the richer `{ fieldErrors, formErrors, format }` result with a separate channel for global (non-field) messages.
|
|
15
|
+
- **Form adapters.** React Hook Form (`mapApiErrors`), Formik (`mapApiErrorsFormik`), TanStack Form v1 (`mapApiErrorsTanstack`), and a plain record (`mapApiErrorsToRecord`). Each is one call at the catch site, no mapping loop. The adapters use structural typing, so no form-library import or peer dependency is required.
|
|
16
|
+
- **Ten built-in parsers.** RFC 7807 / RFC 9457 Problem Details (`errors` map, `violations`, `invalid-params`, top-level `detail`), JSON:API, GraphQL `extensions`, Spring Boot validation, Laravel / DRF field maps, Zod `.flatten()`, and flat object / array shapes. The format is detected automatically. Register your own with the `parsers` option.
|
|
17
|
+
- **Response unwrapping.** Detects `{ status, body }` (`ApiError` from `openapi-zod-ts`), `error.response.data` (Axios), and generic `{ data }` wrappers automatically.
|
|
18
|
+
- **i18n and status filtering.** `resolveMessage` translates every message before it is returned; `statusCodes` restricts parsing to specific HTTP status codes.
|
|
19
|
+
- **Never throws.** All functions return empty results for unrecognized or null input.
|
|
20
|
+
- **Zero runtime dependencies.** Pure TypeScript, full type declarations, nothing third-party added to your bundle.
|
|
17
21
|
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
@@ -21,6 +25,19 @@ Backend API errors don't automatically map to form field errors. Every project w
|
|
|
21
25
|
npm install @codewithagents/api-errors
|
|
22
26
|
```
|
|
23
27
|
|
|
28
|
+
## Public API
|
|
29
|
+
|
|
30
|
+
Six functions cover every use case. The core pair parses any error shape; the adapters wrap it for a specific form library.
|
|
31
|
+
|
|
32
|
+
| Function | What it does |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `extractErrors(error, options?)` | Parses any error shape. Returns `{ fieldErrors, formErrors, format }`. Keeps global errors in a dedicated `formErrors` channel and reports the matched `format`. Recommended for new code. |
|
|
35
|
+
| `extractFieldErrors(error, options?)` | Parses any error shape. Returns normalized `FieldError[]`. Global errors fall back to `fallbackField`. Framework-agnostic. |
|
|
36
|
+
| `mapApiErrors(error, setError, options?)` | React Hook Form adapter. Calls `setError` once per field with `type: 'server'`. |
|
|
37
|
+
| `mapApiErrorsToRecord(error, options?)` | Vanilla adapter. Returns a `Record<field, message>` of each field's first message. |
|
|
38
|
+
| `mapApiErrorsFormik(error, setErrors, options?)` | Formik adapter. Calls `setErrors` with the record from `mapApiErrorsToRecord`. |
|
|
39
|
+
| `mapApiErrorsTanstack(error, form, options?)` | TanStack Form v1 adapter. Calls `form.setFieldMeta` per field to inject server errors. |
|
|
40
|
+
|
|
24
41
|
## Usage
|
|
25
42
|
|
|
26
43
|
### With React Hook Form
|
|
@@ -51,9 +68,7 @@ function SignupForm() {
|
|
|
51
68
|
}
|
|
52
69
|
```
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`mapApiErrors` accepts any function matching `(field: string, error: { type: string; message: string }) => void`, which is compatible with RHF's `UseFormSetError<T>`. No casting needed:
|
|
71
|
+
`mapApiErrors` accepts any function matching `(field: string, error: { type: string; message: string }) => void`, which is compatible with RHF's `UseFormSetError<T>`. No casting needed for typed forms:
|
|
57
72
|
|
|
58
73
|
```tsx
|
|
59
74
|
type FormValues = { email: string; name: string }
|
|
@@ -64,6 +79,40 @@ const { setError } = useForm<FormValues>()
|
|
|
64
79
|
mapApiErrors(error, setError)
|
|
65
80
|
```
|
|
66
81
|
|
|
82
|
+
### With Formik
|
|
83
|
+
|
|
84
|
+
No Formik import is required. The `setErrors` parameter is typed by shape only.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useFormik } from 'formik'
|
|
88
|
+
import { mapApiErrorsFormik } from '@codewithagents/api-errors'
|
|
89
|
+
|
|
90
|
+
const formik = useFormik({
|
|
91
|
+
initialValues: { email: '', name: '' },
|
|
92
|
+
onSubmit: async (values, { setErrors }) => {
|
|
93
|
+
try {
|
|
94
|
+
await createUser(values)
|
|
95
|
+
} catch (error) {
|
|
96
|
+
mapApiErrorsFormik(error, setErrors)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### With TanStack Form v1
|
|
103
|
+
|
|
104
|
+
No TanStack Form import is required. The `form` parameter is typed by shape only.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { mapApiErrorsTanstack } from '@codewithagents/api-errors'
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await form.handleSubmit()
|
|
111
|
+
} catch (error) {
|
|
112
|
+
mapApiErrorsTanstack(error, form)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
67
116
|
### With native `fetch` (no Axios)
|
|
68
117
|
|
|
69
118
|
Pass the parsed response body directly to `extractFieldErrors`:
|
|
@@ -83,7 +132,7 @@ if (!res.ok) {
|
|
|
83
132
|
|
|
84
133
|
### Standalone with `extractFieldErrors`
|
|
85
134
|
|
|
86
|
-
Use `extractFieldErrors` when you need the normalized
|
|
135
|
+
Use `extractFieldErrors` when you need the normalized list without a form library:
|
|
87
136
|
|
|
88
137
|
```ts
|
|
89
138
|
import { extractFieldErrors } from '@codewithagents/api-errors'
|
|
@@ -99,69 +148,167 @@ try {
|
|
|
99
148
|
}
|
|
100
149
|
```
|
|
101
150
|
|
|
102
|
-
###
|
|
151
|
+
### Separating global errors with `extractErrors`
|
|
152
|
+
|
|
153
|
+
`extractFieldErrors` folds non-field messages into `fallbackField`. When you want to render global errors (for example "You do not have permission") in their own banner, use `extractErrors`. It keeps them in a separate `formErrors` channel and reports the matched `format`, so you can distinguish an unrecognized body (`format` is `null`) from a recognized body with no errors.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { extractErrors } from '@codewithagents/api-errors'
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await createUser(data)
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const { fieldErrors, formErrors, format } = extractErrors(error)
|
|
162
|
+
|
|
163
|
+
if (format === null) {
|
|
164
|
+
// Unrecognized shape: show a generic banner
|
|
165
|
+
showBanner('An unexpected error occurred.')
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const { field, message } of fieldErrors) {
|
|
170
|
+
setError(field, { type: 'server', message })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (formErrors.length > 0) {
|
|
174
|
+
setFormBanner(formErrors.join(' '))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`ExtractResult` is the shape returned by `extractErrors`:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
interface ExtractResult {
|
|
183
|
+
fieldErrors: FieldError[]
|
|
184
|
+
formErrors: string[]
|
|
185
|
+
format: ErrorFormat | null
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Options
|
|
190
|
+
|
|
191
|
+
Every function accepts a `MapApiErrorsOptions` object.
|
|
103
192
|
|
|
104
|
-
|
|
193
|
+
| Option | Type | Behaviour |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| `fallbackField` | `string` | Field name used when no field can be determined. Defaults to `'root'`. |
|
|
196
|
+
| `transformField` | `(field: string) => string` | Applied to every resolved field name, and to `fallbackField`. Maps backend camelCase to nested RHF dot-paths. |
|
|
197
|
+
| `statusCodes` | `number[]` | Restricts parsing to these HTTP status codes. If a detectable status is not in the list, the call returns empty immediately. If no status is detectable, parsing proceeds normally. |
|
|
198
|
+
| `resolveMessage` | `(message: string, field: string \| null) => string` | i18n / message reformat hook. Called for every field and form error before it is returned. `field` is `null` for global errors. |
|
|
199
|
+
| `parsers` | `ReadonlyArray<CustomParser>` | Custom parsers tried before the built-in ones, in order. The first non-null result wins. They receive the already-unwrapped body. |
|
|
105
200
|
|
|
106
201
|
```ts
|
|
107
202
|
const fieldErrors = extractFieldErrors(error, {
|
|
108
203
|
// Field name used when no field can be determined (default: 'root')
|
|
109
204
|
fallbackField: 'serverError',
|
|
110
205
|
|
|
111
|
-
// Transform field names, e.g. camelCase backend
|
|
206
|
+
// Transform field names, e.g. camelCase backend to dot.path for nested RHF fields
|
|
112
207
|
transformField: (f) => f.replace(/([A-Z])/g, '.$1').toLowerCase(),
|
|
113
208
|
})
|
|
114
209
|
|
|
115
|
-
// Options work on
|
|
116
|
-
mapApiErrors(error, setError, {
|
|
210
|
+
// Options work on every function
|
|
211
|
+
mapApiErrors(error, setError, { statusCodes: [422] })
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **Note:** `transformField` is also applied to `fallbackField`. If you transform `emailAddress` to `email.address`, the fallback field name is transformed the same way.
|
|
215
|
+
|
|
216
|
+
### Localizing messages with `resolveMessage`
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { extractErrors } from '@codewithagents/api-errors'
|
|
220
|
+
|
|
221
|
+
extractErrors(error, {
|
|
222
|
+
resolveMessage: (msg, field) => t(`errors.${msg}`, { defaultValue: msg }),
|
|
223
|
+
})
|
|
117
224
|
```
|
|
118
225
|
|
|
119
|
-
|
|
226
|
+
### Custom parsers
|
|
120
227
|
|
|
121
|
-
|
|
228
|
+
Register parsers for application-specific shapes. They run before the built-in parsers; return `null` to let the next one try.
|
|
122
229
|
|
|
123
|
-
|
|
230
|
+
```ts
|
|
231
|
+
import { extractErrors } from '@codewithagents/api-errors'
|
|
232
|
+
import type { CustomParser, ParsedErrors } from '@codewithagents/api-errors'
|
|
233
|
+
|
|
234
|
+
const myParser: CustomParser = (body): ParsedErrors | null => {
|
|
235
|
+
if (typeof body !== 'object' || body === null || !('myErrors' in body)) return null
|
|
236
|
+
const fieldErrors = (body as any).myErrors.map((e: any) => ({ field: e.key, message: e.text }))
|
|
237
|
+
return { fieldErrors, formErrors: [] }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
extractErrors(error, { parsers: [myParser] })
|
|
241
|
+
```
|
|
124
242
|
|
|
125
|
-
|
|
243
|
+
## Supported error formats
|
|
244
|
+
|
|
245
|
+
The format is detected automatically. Custom parsers run first; if the body is an array, the flat-array parser runs; otherwise the built-in object parsers are tried in order until the first match. See the [full format reference](https://openapi.codewithagents.de/api-errors#supported-error-formats) for the complete precedence rules and edge cases.
|
|
246
|
+
|
|
247
|
+
| Format | Recognized by |
|
|
248
|
+
| --- | --- |
|
|
249
|
+
| RFC 7807 / RFC 9457 Problem Details | `errors` object map of `field -> string \| string[]` (Spring Boot 3+, standard Problem Details) |
|
|
250
|
+
| RFC 9457 `violations` | `violations: [{ field, message }]` array |
|
|
251
|
+
| RFC 9457 `invalid-params` | `"invalid-params": [{ name, reason }]` array |
|
|
252
|
+
| RFC 9457 `detail` | top-level `detail` string (last-resort fallback) |
|
|
253
|
+
| JSON:API | `errors: [{ source: { pointer }, detail }]` array |
|
|
254
|
+
| GraphQL | `errors: [{ message, extensions: { field \| path } }]` array |
|
|
255
|
+
| Spring Boot (pre-3) | `errors: [{ field, defaultMessage }]` array |
|
|
256
|
+
| Laravel / DRF | top-level field map `{ field: string \| string[] }` |
|
|
257
|
+
| Zod `.flatten()` | `{ fieldErrors: { field: string[] }, formErrors: string[] }` |
|
|
258
|
+
| Flat object / array | `{ field, message }` or `[{ field, message }]` |
|
|
259
|
+
|
|
260
|
+
**RFC 7807 / RFC 9457 Problem Details with an `errors` map:**
|
|
126
261
|
```json
|
|
127
262
|
{
|
|
128
|
-
"type": "
|
|
263
|
+
"type": "https://example.com/errors/validation",
|
|
129
264
|
"title": "Validation failed",
|
|
130
265
|
"status": 400,
|
|
131
266
|
"errors": {
|
|
132
267
|
"email": ["must not be blank"],
|
|
133
|
-
"name": ["too short"]
|
|
268
|
+
"name": ["too short", "must not contain numbers"]
|
|
134
269
|
}
|
|
135
270
|
}
|
|
136
271
|
```
|
|
137
272
|
|
|
138
|
-
**
|
|
273
|
+
**RFC 9457 `violations` array:**
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"status": 422,
|
|
277
|
+
"violations": [{ "field": "email", "message": "must not be blank" }]
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Spring Boot default validation format (pre-3):**
|
|
139
282
|
```json
|
|
140
283
|
{
|
|
141
284
|
"status": 400,
|
|
142
|
-
"errors": [
|
|
143
|
-
{ "field": "email", "defaultMessage": "must not be blank" }
|
|
144
|
-
]
|
|
285
|
+
"errors": [{ "field": "email", "defaultMessage": "must not be blank" }]
|
|
145
286
|
}
|
|
146
287
|
```
|
|
147
288
|
|
|
148
|
-
**
|
|
289
|
+
**Zod `.flatten()` output:**
|
|
149
290
|
```json
|
|
150
|
-
{
|
|
291
|
+
{
|
|
292
|
+
"fieldErrors": { "email": ["Invalid email"] },
|
|
293
|
+
"formErrors": ["At least one field must be provided."]
|
|
294
|
+
}
|
|
151
295
|
```
|
|
152
296
|
|
|
153
|
-
**
|
|
297
|
+
**Simple flat formats (custom APIs):**
|
|
298
|
+
```json
|
|
299
|
+
{ "field": "email", "message": "Invalid email" }
|
|
300
|
+
```
|
|
154
301
|
```json
|
|
155
302
|
[{ "field": "email", "message": "Invalid email" }]
|
|
156
303
|
```
|
|
157
304
|
|
|
158
|
-
Response wrappers are
|
|
305
|
+
Response wrappers are unwrapped before parsing: `{ status, body }` (`ApiError` from `openapi-zod-ts`), Axios-style `error.response.data`, and generic `{ data: { ... } }` shapes (only when `data` is a plain object).
|
|
159
306
|
|
|
160
307
|
## Known behaviour: multiple errors for the same field
|
|
161
308
|
|
|
162
309
|
See the [multiple errors per field](https://openapi.codewithagents.de/api-errors#multiple-errors-per-field) section in the docs for a full example including grouping with `Map.groupBy`.
|
|
163
310
|
|
|
164
|
-
When a backend returns multiple errors for the same field
|
|
311
|
+
When a backend returns multiple errors for the same field, `extractFieldErrors` returns all of them. If you pass them all to React Hook Form's `setError`, the **last call wins**; only the last message is displayed. To show all messages, group them before calling `setError`:
|
|
165
312
|
|
|
166
313
|
```ts
|
|
167
314
|
const fieldErrors = extractFieldErrors(error)
|
|
@@ -172,10 +319,32 @@ for (const [field, errs] of grouped) {
|
|
|
172
319
|
}
|
|
173
320
|
```
|
|
174
321
|
|
|
322
|
+
`mapApiErrorsToRecord` (and therefore the Formik and TanStack adapters) keeps each field's **first** message instead.
|
|
323
|
+
|
|
324
|
+
## Ecosystem
|
|
325
|
+
|
|
326
|
+
`api-errors` is the runtime helper in the [openapi-zod-ts](https://github.com/codewithagents/openapi-zod-ts) toolchain. The four generators turn one OpenAPI 3.1 spec into the types, client, hooks, and mocks your app uses; `api-errors` maps the error envelopes those servers return back onto your form fields.
|
|
327
|
+
|
|
328
|
+
| Package | Role |
|
|
329
|
+
| --- | --- |
|
|
330
|
+
| [`openapi-zod-ts`](https://www.npmjs.com/package/openapi-zod-ts) | Generator: TypeScript models, native fetch client, and Zod schemas. Emits the `ApiError` class that `api-errors` unwraps automatically. |
|
|
331
|
+
| [`@codewithagents/openapi-server`](https://www.npmjs.com/package/@codewithagents/openapi-server) | Generator: framework-agnostic service interface plus optional `hono \| express \| fastify \| none` router. Emits the RFC 7807 / RFC 9457 envelopes `api-errors` parses. |
|
|
332
|
+
| [`@codewithagents/openapi-react-query`](https://www.npmjs.com/package/@codewithagents/openapi-react-query) | Generator: typed React Query v5 hooks. Call `mapApiErrors` in a `useMutation` `onError` to wire server validation to form fields. |
|
|
333
|
+
| [`@codewithagents/openapi-msw`](https://www.npmjs.com/package/@codewithagents/openapi-msw) | Generator: MSW v2 handlers with seeded Faker mock data, including error responses to exercise this package against. |
|
|
334
|
+
| **`@codewithagents/api-errors`** | Runtime helper (this package): maps server error envelopes to form-field errors. |
|
|
335
|
+
|
|
336
|
+
See the [petstore-fastify demo](https://github.com/codewithagents/openapi-zod-ts/tree/main/packages/petstore-fastify) for the canonical full-stack example that wires these together, including a cross-field validation error round-tripped from the server onto a form field.
|
|
337
|
+
|
|
175
338
|
## TypeScript
|
|
176
339
|
|
|
177
340
|
This package is written in TypeScript and ships full type declarations. No `@types` package needed.
|
|
178
341
|
|
|
179
342
|
```ts
|
|
180
|
-
import type {
|
|
343
|
+
import type {
|
|
344
|
+
FieldError,
|
|
345
|
+
MapApiErrorsOptions,
|
|
346
|
+
ExtractResult,
|
|
347
|
+
ErrorFormat,
|
|
348
|
+
CustomParser,
|
|
349
|
+
} from '@codewithagents/api-errors'
|
|
181
350
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
export interface FieldError {
|
|
5
|
-
field: string;
|
|
6
|
-
message: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* The format/shape that was recognized when parsing an error body.
|
|
10
|
-
*
|
|
11
|
-
* - `'rfc7807-map'` - RFC 7807 / RFC 9457 `errors` map: `{ errors: { field: [...] } }`
|
|
12
|
-
* - `'spring-array'` - Spring Boot array: `{ errors: [{ field, defaultMessage }] }`
|
|
13
|
-
* - `'violations'` - RFC 9457 / Spring `violations` array: `{ violations: [{ field, message }] }`
|
|
14
|
-
* - `'invalid-params'` - RFC 9457 `invalid-params` array: `{ "invalid-params": [{ name, reason }] }`
|
|
15
|
-
* - `'json-api'` - JSON:API errors array: `{ errors: [{ source: { pointer }, detail }] }`
|
|
16
|
-
* - `'laravel-drf'` - Laravel / DRF top-level field map: `{ field: ["msg1", ...] }`
|
|
17
|
-
* - `'flat-object'` - Simple flat object: `{ field, message }`
|
|
18
|
-
* - `'flat-array'` - Array of flat objects: `[{ field, message }]`
|
|
19
|
-
* - `'rfc9457-detail'` - RFC 9457 top-level `detail` string (last-resort fallback)
|
|
20
|
-
* - `'custom'` - Matched a caller-provided custom parser
|
|
21
|
-
*/
|
|
22
|
-
export type ErrorFormat = 'rfc7807-map' | 'spring-array' | 'violations' | 'invalid-params' | 'json-api' | 'laravel-drf' | 'flat-object' | 'flat-array' | 'rfc9457-detail' | 'custom';
|
|
1
|
+
export type { FieldError, ParsedErrors, ErrorFormat, CustomParser } from './parsers/types.js';
|
|
2
|
+
import type { FieldError, ErrorFormat, CustomParser } from './parsers/types.js';
|
|
23
3
|
/**
|
|
24
4
|
* Rich result returned by `extractErrors`.
|
|
25
5
|
*/
|
|
@@ -43,25 +23,6 @@ export interface ExtractResult {
|
|
|
43
23
|
*/
|
|
44
24
|
format: ErrorFormat | null;
|
|
45
25
|
}
|
|
46
|
-
/**
|
|
47
|
-
* A custom parser that callers can register to handle application-specific
|
|
48
|
-
* error shapes not covered by the built-in parsers.
|
|
49
|
-
*
|
|
50
|
-
* Return `null` to indicate "not recognized" and let the next parser try.
|
|
51
|
-
* Return a `ParsedErrors` object (even if both arrays are empty) to signal
|
|
52
|
-
* that this shape was recognized.
|
|
53
|
-
*/
|
|
54
|
-
export interface CustomParser {
|
|
55
|
-
(body: unknown): ParsedErrors | null;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Raw parse result used internally and returned by custom parsers.
|
|
59
|
-
* Both arrays may be empty if the shape was recognized but contained no errors.
|
|
60
|
-
*/
|
|
61
|
-
export interface ParsedErrors {
|
|
62
|
-
fieldErrors: FieldError[];
|
|
63
|
-
formErrors: string[];
|
|
64
|
-
}
|
|
65
26
|
/**
|
|
66
27
|
* Options for mapApiErrors and extractFieldErrors.
|
|
67
28
|
*/
|
|
@@ -174,4 +135,63 @@ export declare function mapApiErrors(error: unknown, setError: (field: string, e
|
|
|
174
135
|
type: string;
|
|
175
136
|
message: string;
|
|
176
137
|
}) => void, options?: MapApiErrorsOptions): void;
|
|
138
|
+
/**
|
|
139
|
+
* Vanilla (framework-agnostic) adapter.
|
|
140
|
+
*
|
|
141
|
+
* Extracts field errors from an unknown API error response and returns a plain
|
|
142
|
+
* `Record<string, string>` mapping field names to their first error message.
|
|
143
|
+
* Useful for any custom form library or imperative error handling.
|
|
144
|
+
*
|
|
145
|
+
* Only fieldErrors are included. For form-level (global) errors use `extractErrors`.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* const record = mapApiErrorsToRecord(error)
|
|
150
|
+
* // { email: "must not be blank", name: "required" }
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export declare function mapApiErrorsToRecord(error: unknown, options?: MapApiErrorsOptions): Record<string, string>;
|
|
154
|
+
/**
|
|
155
|
+
* Formik adapter.
|
|
156
|
+
*
|
|
157
|
+
* Extracts field errors from an unknown API error response and calls Formik's
|
|
158
|
+
* `setErrors` with a `Record<string, string>` of field -> first error message.
|
|
159
|
+
*
|
|
160
|
+
* No Formik import is required. The `setErrors` parameter is typed by its
|
|
161
|
+
* shape only (structural typing).
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* try {
|
|
166
|
+
* await submitForm(values)
|
|
167
|
+
* } catch (error) {
|
|
168
|
+
* mapApiErrorsFormik(error, setErrors)
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export declare function mapApiErrorsFormik(error: unknown, setErrors: (errors: Record<string, string>) => void, options?: MapApiErrorsOptions): void;
|
|
173
|
+
/**
|
|
174
|
+
* TanStack Form v1 adapter.
|
|
175
|
+
*
|
|
176
|
+
* Extracts field errors from an unknown API error response and calls
|
|
177
|
+
* `form.setFieldMeta` for each field to inject server-side errors into the
|
|
178
|
+
* form state. The updater function merges errors and errorMap onto the
|
|
179
|
+
* existing field meta.
|
|
180
|
+
*
|
|
181
|
+
* No TanStack Form import is required. The `form` parameter is typed by its
|
|
182
|
+
* shape only (structural typing), so this adapter works without adding
|
|
183
|
+
* `@tanstack/form-core` as a dependency.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* try {
|
|
188
|
+
* await form.handleSubmit()
|
|
189
|
+
* } catch (error) {
|
|
190
|
+
* mapApiErrorsTanstack(error, form)
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function mapApiErrorsTanstack(error: unknown, form: {
|
|
195
|
+
setFieldMeta: (field: string, updater: (meta: any) => any) => void;
|
|
196
|
+
}, options?: MapApiErrorsOptions): void;
|
|
177
197
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC7F,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAM/E;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB;;;;;OAKG;IACH,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;CAC3B;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;IAC1C;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,MAAM,CAAA;IAClE;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;CACtC;AA4BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,aAAa,CA2B1F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,UAAU,EAAE,CAqB9F;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;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,EACnD,OAAO,CAAC,EAAE,mBAAmB,GAC5B,IAAI,CAEN;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE;IAAE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,KAAK,IAAI,CAAA;CAAE,EAC5E,OAAO,CAAC,EAAE,mBAAmB,GAC5B,IAAI,CASN"}
|