@codewithagents/api-errors 1.0.6 → 1.2.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.
Files changed (57) hide show
  1. package/dist/index.d.ts +133 -7
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +163 -181
  4. package/dist/index.js.map +1 -1
  5. package/dist/parsers/dispatch.d.ts +12 -0
  6. package/dist/parsers/dispatch.d.ts.map +1 -0
  7. package/dist/parsers/dispatch.js +116 -0
  8. package/dist/parsers/dispatch.js.map +1 -0
  9. package/dist/parsers/flat.d.ts +12 -0
  10. package/dist/parsers/flat.d.ts.map +1 -0
  11. package/dist/parsers/flat.js +31 -0
  12. package/dist/parsers/flat.js.map +1 -0
  13. package/dist/parsers/graphql-extensions.d.ts +18 -0
  14. package/dist/parsers/graphql-extensions.d.ts.map +1 -0
  15. package/dist/parsers/graphql-extensions.js +83 -0
  16. package/dist/parsers/graphql-extensions.js.map +1 -0
  17. package/dist/parsers/invalid-params.d.ts +7 -0
  18. package/dist/parsers/invalid-params.d.ts.map +1 -0
  19. package/dist/parsers/invalid-params.js +12 -0
  20. package/dist/parsers/invalid-params.js.map +1 -0
  21. package/dist/parsers/json-api.d.ts +14 -0
  22. package/dist/parsers/json-api.d.ts.map +1 -0
  23. package/dist/parsers/json-api.js +85 -0
  24. package/dist/parsers/json-api.js.map +1 -0
  25. package/dist/parsers/laravel-drf.d.ts +10 -0
  26. package/dist/parsers/laravel-drf.d.ts.map +1 -0
  27. package/dist/parsers/laravel-drf.js +46 -0
  28. package/dist/parsers/laravel-drf.js.map +1 -0
  29. package/dist/parsers/rfc7807.d.ts +7 -0
  30. package/dist/parsers/rfc7807.d.ts.map +1 -0
  31. package/dist/parsers/rfc7807.js +24 -0
  32. package/dist/parsers/rfc7807.js.map +1 -0
  33. package/dist/parsers/spring.d.ts +7 -0
  34. package/dist/parsers/spring.d.ts.map +1 -0
  35. package/dist/parsers/spring.js +24 -0
  36. package/dist/parsers/spring.js.map +1 -0
  37. package/dist/parsers/types.d.ts +44 -0
  38. package/dist/parsers/types.d.ts.map +1 -0
  39. package/dist/parsers/types.js +2 -0
  40. package/dist/parsers/types.js.map +1 -0
  41. package/dist/parsers/unwrap.d.ts +3 -0
  42. package/dist/parsers/unwrap.d.ts.map +1 -0
  43. package/dist/parsers/unwrap.js +29 -0
  44. package/dist/parsers/unwrap.js.map +1 -0
  45. package/dist/parsers/utils.d.ts +12 -0
  46. package/dist/parsers/utils.d.ts.map +1 -0
  47. package/dist/parsers/utils.js +65 -0
  48. package/dist/parsers/utils.js.map +1 -0
  49. package/dist/parsers/violations.d.ts +7 -0
  50. package/dist/parsers/violations.d.ts.map +1 -0
  51. package/dist/parsers/violations.js +12 -0
  52. package/dist/parsers/violations.js.map +1 -0
  53. package/dist/parsers/zod-flatten.d.ts +11 -0
  54. package/dist/parsers/zod-flatten.d.ts.map +1 -0
  55. package/dist/parsers/zod-flatten.js +37 -0
  56. package/dist/parsers/zod-flatten.js.map +1 -0
  57. package/package.json +4 -1
@@ -0,0 +1,116 @@
1
+ import { isObject } from './utils.js';
2
+ import { tryParseViolations } from './violations.js';
3
+ import { tryParseInvalidParams } from './invalid-params.js';
4
+ import { tryParseJsonApi } from './json-api.js';
5
+ import { tryParseRfc7807 } from './rfc7807.js';
6
+ import { tryParseSpringArray } from './spring.js';
7
+ import { tryParseGraphqlExtensions } from './graphql-extensions.js';
8
+ import { tryParseLaravelDrf } from './laravel-drf.js';
9
+ import { tryParseFlatObject, tryParseFlatArray } from './flat.js';
10
+ import { tryParseZodFlatten } from './zod-flatten.js';
11
+ /** Wrap a ParsedErrors result with its format tag, applying transformField to field names. */
12
+ function wrapMatch(parsed, format, transformField) {
13
+ return {
14
+ parsed: {
15
+ fieldErrors: parsed.fieldErrors.map((e) => ({
16
+ field: transformField(e.field),
17
+ message: e.message,
18
+ })),
19
+ formErrors: parsed.formErrors,
20
+ },
21
+ format,
22
+ };
23
+ }
24
+ /**
25
+ * Try all object-body parsers in priority order.
26
+ * Returns the first match, or null if no parser recognized the shape.
27
+ */
28
+ function matchObjectBody(body, fallbackField, transformField) {
29
+ // Each thunk in this ordered list tries one parser and returns a MatchResult or null.
30
+ const candidates = [
31
+ () => {
32
+ const r = tryParseViolations(body);
33
+ return r && wrapMatch(r, 'violations', transformField);
34
+ },
35
+ () => {
36
+ const r = tryParseInvalidParams(body);
37
+ return r && wrapMatch(r, 'invalid-params', transformField);
38
+ },
39
+ () => {
40
+ const r = tryParseJsonApi(body);
41
+ return r && wrapMatch(r, 'json-api', transformField);
42
+ },
43
+ () => {
44
+ const r = tryParseRfc7807(body, transformField);
45
+ return r && { parsed: { fieldErrors: r, formErrors: [] }, format: 'rfc7807-map' };
46
+ },
47
+ () => {
48
+ const r = tryParseGraphqlExtensions(body);
49
+ return r && wrapMatch(r, 'graphql-extensions', transformField);
50
+ },
51
+ () => {
52
+ const r = tryParseSpringArray(body, fallbackField, transformField);
53
+ return r && { parsed: { fieldErrors: r, formErrors: [] }, format: 'spring-array' };
54
+ },
55
+ () => {
56
+ const r = tryParseLaravelDrf(body);
57
+ return r && wrapMatch(r, 'laravel-drf', transformField);
58
+ },
59
+ () => {
60
+ const r = tryParseFlatObject(body, fallbackField, transformField);
61
+ return r && { parsed: { fieldErrors: r, formErrors: [] }, format: 'flat-object' };
62
+ },
63
+ () => {
64
+ const r = tryParseZodFlatten(body);
65
+ return r && wrapMatch(r, 'zod-flatten', transformField);
66
+ },
67
+ () => {
68
+ const detail = body['detail'];
69
+ return typeof detail === 'string'
70
+ ? { parsed: { fieldErrors: [], formErrors: [detail] }, format: 'rfc9457-detail' }
71
+ : null;
72
+ },
73
+ ];
74
+ for (const candidate of candidates) {
75
+ const result = candidate();
76
+ if (result !== null)
77
+ return result;
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * Run custom parsers (if any) before the built-in ones.
83
+ * Returns the first match, or null to fall through.
84
+ */
85
+ function tryCustomParsers(body, customParsers) {
86
+ for (const parser of customParsers) {
87
+ const parsed = parser(body);
88
+ if (parsed !== null)
89
+ return { parsed, format: 'custom' };
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Dispatch an array body through the flat-array parser.
95
+ */
96
+ function matchArrayBody(body, fallbackField, transformField) {
97
+ const flat = tryParseFlatArray(body, fallbackField, transformField);
98
+ return flat ? { parsed: { fieldErrors: flat, formErrors: [] }, format: 'flat-array' } : null;
99
+ }
100
+ /**
101
+ * Run all parsers (custom, array, object) against an already-unwrapped body.
102
+ * Returns the first match, or null if no parser recognized the shape.
103
+ */
104
+ export function matchBody(body, fallbackField, transformField, customParsers) {
105
+ if (customParsers?.length) {
106
+ const custom = tryCustomParsers(body, customParsers);
107
+ if (custom !== null)
108
+ return custom;
109
+ }
110
+ if (Array.isArray(body))
111
+ return matchArrayBody(body, fallbackField, transformField);
112
+ if (!isObject(body))
113
+ return null;
114
+ return matchObjectBody(body, fallbackField, transformField);
115
+ }
116
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/parsers/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { FieldError } from './types.js';
2
+ /**
3
+ * Simple flat object format:
4
+ * { "field": "email", "message": "Invalid email" }
5
+ */
6
+ export declare function tryParseFlatObject(body: Record<string, unknown>, fallbackField: string, transformField: (field: string) => string): FieldError[] | null;
7
+ /**
8
+ * Array of simple flat objects:
9
+ * [{ "field": "email", "message": "Invalid email" }]
10
+ */
11
+ export declare function tryParseFlatArray(body: unknown[], fallbackField: string, transformField: (field: string) => string): FieldError[] | null;
12
+ //# sourceMappingURL=flat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flat.d.ts","sourceRoot":"","sources":["../../src/parsers/flat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACxC,UAAU,EAAE,GAAG,IAAI,CAMrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EAAE,EACf,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACxC,UAAU,EAAE,GAAG,IAAI,CAUrB"}
@@ -0,0 +1,31 @@
1
+ import { isObject } from './utils.js';
2
+ /**
3
+ * Simple flat object format:
4
+ * { "field": "email", "message": "Invalid email" }
5
+ */
6
+ export function tryParseFlatObject(body, fallbackField, transformField) {
7
+ const rawField = typeof body['field'] === 'string' ? body['field'] : null;
8
+ const message = typeof body['message'] === 'string' ? body['message'] : null;
9
+ if (message === null)
10
+ return null;
11
+ const field = rawField !== null ? rawField : fallbackField;
12
+ return [{ field: transformField(field), message }];
13
+ }
14
+ /**
15
+ * Array of simple flat objects:
16
+ * [{ "field": "email", "message": "Invalid email" }]
17
+ */
18
+ export function tryParseFlatArray(body, fallbackField, transformField) {
19
+ const result = [];
20
+ for (const item of body) {
21
+ if (!isObject(item))
22
+ continue;
23
+ const rawField = typeof item['field'] === 'string' ? item['field'] : fallbackField;
24
+ const message = typeof item['message'] === 'string' ? item['message'] : null;
25
+ if (message === null)
26
+ continue;
27
+ result.push({ field: transformField(rawField), message });
28
+ }
29
+ return result.length > 0 ? result : null;
30
+ }
31
+ //# sourceMappingURL=flat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flat.js","sourceRoot":"","sources":["../../src/parsers/flat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAA6B,EAC7B,aAAqB,EACrB,cAAyC;IAEzC,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACzE,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5E,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,KAAK,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAA;IAC1D,OAAO,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAe,EACf,aAAqB,EACrB,cAAyC;IAEzC,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC7B,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAA;QAClF,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5E,IAAI,OAAO,KAAK,IAAI;YAAE,SAAQ;QAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ParsedErrors } from './types.js';
2
+ /**
3
+ * GraphQL errors array shape:
4
+ * { errors: [{ message: "...", extensions?: { field?: string, path?: string[] } }] }
5
+ *
6
+ * Field resolution (in order):
7
+ * 1. `extensions.field` — plain string field name
8
+ * 2. `extensions.path` — array of path segments joined with `.`
9
+ * 3. Neither — treated as a form-level (global) error
10
+ *
11
+ * Guards (must NOT match):
12
+ * - Items with `source.pointer` or `detail` (JSON:API shape)
13
+ * - Items with `defaultMessage` (Spring Boot shape)
14
+ *
15
+ * Returns null if `body.errors` is not an array.
16
+ */
17
+ export declare function tryParseGraphqlExtensions(body: Record<string, unknown>): ParsedErrors | null;
18
+ //# sourceMappingURL=graphql-extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-extensions.d.ts","sourceRoot":"","sources":["../../src/parsers/graphql-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,YAAY,CAAA;AAG1D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,GAAG,IAAI,CAwB5F"}
@@ -0,0 +1,83 @@
1
+ import { isObject } from './utils.js';
2
+ /**
3
+ * GraphQL errors array shape:
4
+ * { errors: [{ message: "...", extensions?: { field?: string, path?: string[] } }] }
5
+ *
6
+ * Field resolution (in order):
7
+ * 1. `extensions.field` — plain string field name
8
+ * 2. `extensions.path` — array of path segments joined with `.`
9
+ * 3. Neither — treated as a form-level (global) error
10
+ *
11
+ * Guards (must NOT match):
12
+ * - Items with `source.pointer` or `detail` (JSON:API shape)
13
+ * - Items with `defaultMessage` (Spring Boot shape)
14
+ *
15
+ * Returns null if `body.errors` is not an array.
16
+ */
17
+ export function tryParseGraphqlExtensions(body) {
18
+ const { errors } = body;
19
+ if (!Array.isArray(errors))
20
+ return null;
21
+ // Require at least one item that looks like a GraphQL error (has message, not JSON:API/Spring).
22
+ if (!errors.some(isGraphqlErrorItem))
23
+ return null;
24
+ const fieldErrors = [];
25
+ const formErrors = [];
26
+ for (const item of errors) {
27
+ if (!isObject(item))
28
+ continue;
29
+ const message = typeof item['message'] === 'string' ? item['message'] : null;
30
+ if (message === null)
31
+ continue;
32
+ const field = extractField(item);
33
+ if (field !== null) {
34
+ fieldErrors.push({ field, message });
35
+ }
36
+ else {
37
+ formErrors.push(message);
38
+ }
39
+ }
40
+ return { fieldErrors, formErrors };
41
+ }
42
+ /** True when an item looks like a GraphQL error object (not JSON:API, not Spring). */
43
+ function isGraphqlErrorItem(item) {
44
+ if (!isObject(item))
45
+ return false;
46
+ // Must have a string message.
47
+ if (typeof item['message'] !== 'string')
48
+ return false;
49
+ // Must NOT have JSON:API keys.
50
+ if (isObject(item['source']) && 'pointer' in item['source'])
51
+ return false;
52
+ if (typeof item['detail'] === 'string')
53
+ return false;
54
+ // Must NOT have Spring Boot keys.
55
+ if ('defaultMessage' in item)
56
+ return false;
57
+ // Must NOT have a top-level `field` key (Spring uses `field` for the field name).
58
+ // Real GraphQL errors carry the field name in `extensions.field`, not at the top level.
59
+ if ('field' in item)
60
+ return false;
61
+ return true;
62
+ }
63
+ /** Extract a field name from a GraphQL error item's extensions. Returns null for form-level. */
64
+ function extractField(item) {
65
+ const extensions = item['extensions'];
66
+ if (!isObject(extensions))
67
+ return null;
68
+ // Prefer extensions.field (plain string).
69
+ if (typeof extensions['field'] === 'string' && extensions['field'] !== '') {
70
+ return extensions['field'];
71
+ }
72
+ // Fall back to extensions.path (array joined with `.`).
73
+ const path = extensions['path'];
74
+ if (Array.isArray(path) && path.length > 0) {
75
+ const segments = path
76
+ .filter((s) => typeof s === 'string' || typeof s === 'number')
77
+ .map(String);
78
+ if (segments.length > 0)
79
+ return segments.join('.');
80
+ }
81
+ return null;
82
+ }
83
+ //# sourceMappingURL=graphql-extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-extensions.js","sourceRoot":"","sources":["../../src/parsers/graphql-extensions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ParsedErrors } from './types.js';
2
+ /**
3
+ * RFC 9457 invalid-params array:
4
+ * { "invalid-params": [{ "name": "email", "reason": "must not be blank" }] }
5
+ */
6
+ export declare function tryParseInvalidParams(body: Record<string, unknown>): ParsedErrors | null;
7
+ //# sourceMappingURL=invalid-params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-params.d.ts","sourceRoot":"","sources":["../../src/parsers/invalid-params.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,GAAG,IAAI,CAIxF"}
@@ -0,0 +1,12 @@
1
+ import { parseKeyedArray } from './utils.js';
2
+ /**
3
+ * RFC 9457 invalid-params array:
4
+ * { "invalid-params": [{ "name": "email", "reason": "must not be blank" }] }
5
+ */
6
+ export function tryParseInvalidParams(body) {
7
+ const invalidParams = body['invalid-params'];
8
+ if (!Array.isArray(invalidParams))
9
+ return null;
10
+ return parseKeyedArray(invalidParams, 'name', 'reason');
11
+ }
12
+ //# sourceMappingURL=invalid-params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-params.js","sourceRoot":"","sources":["../../src/parsers/invalid-params.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA6B;IACjE,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAC5C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9C,OAAO,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AACzD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ParsedErrors } from './types.js';
2
+ /**
3
+ * JSON:API errors array:
4
+ * { "errors": [{ "source": { "pointer": "/data/attributes/email" }, "detail": "..." }] }
5
+ *
6
+ * Errors with a source pointer map to field errors.
7
+ * Errors without a source pointer (or with a pointer of "/") map to form errors.
8
+ *
9
+ * IMPORTANT: this parser must run BEFORE tryParseSpringArray because a JSON:API
10
+ * body also has an `errors` array and would silently match the Spring-array parser
11
+ * via the defaultMessage fallback.
12
+ */
13
+ export declare function tryParseJsonApi(body: Record<string, unknown>): ParsedErrors | null;
14
+ //# sourceMappingURL=json-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-api.d.ts","sourceRoot":"","sources":["../../src/parsers/json-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,YAAY,CAAA;AAG1D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,GAAG,IAAI,CAWlF"}
@@ -0,0 +1,85 @@
1
+ import { isObject } from './utils.js';
2
+ /**
3
+ * JSON:API errors array:
4
+ * { "errors": [{ "source": { "pointer": "/data/attributes/email" }, "detail": "..." }] }
5
+ *
6
+ * Errors with a source pointer map to field errors.
7
+ * Errors without a source pointer (or with a pointer of "/") map to form errors.
8
+ *
9
+ * IMPORTANT: this parser must run BEFORE tryParseSpringArray because a JSON:API
10
+ * body also has an `errors` array and would silently match the Spring-array parser
11
+ * via the defaultMessage fallback.
12
+ */
13
+ export function tryParseJsonApi(body) {
14
+ const { errors } = body;
15
+ if (!Array.isArray(errors) || !isJsonApiShape(errors))
16
+ return null;
17
+ const fieldErrors = [];
18
+ const formErrors = [];
19
+ for (const item of errors) {
20
+ if (!isObject(item))
21
+ continue;
22
+ classifyJsonApiItem(item, fieldErrors, formErrors);
23
+ }
24
+ return { fieldErrors, formErrors };
25
+ }
26
+ /** True when at least one item looks like a JSON:API error object (not Spring). */
27
+ function isJsonApiShape(errors) {
28
+ return errors.some((item) => isObject(item) &&
29
+ (isObject(item['source']) || typeof item['detail'] === 'string') &&
30
+ !('field' in item) &&
31
+ !('defaultMessage' in item));
32
+ }
33
+ /** Push one JSON:API error item into the appropriate bucket. */
34
+ function classifyJsonApiItem(item, fieldErrors, formErrors) {
35
+ const detail = typeof item['detail'] === 'string' ? item['detail'] : null;
36
+ const message = detail ?? 'Unknown error';
37
+ const source = item['source'];
38
+ if (isObject(source) && typeof source['pointer'] === 'string') {
39
+ const field = pointerToField(source['pointer']);
40
+ if (field !== null) {
41
+ fieldErrors.push({ field, message });
42
+ }
43
+ else if (detail !== null) {
44
+ formErrors.push(detail);
45
+ }
46
+ }
47
+ else if (detail !== null) {
48
+ formErrors.push(detail);
49
+ }
50
+ }
51
+ /**
52
+ * Convert a JSON Pointer (RFC 6901) used in JSON:API source.pointer to a
53
+ * dot-separated field path for form use.
54
+ *
55
+ * `/data/attributes/email` -> `email`
56
+ * `/data/attributes/address/city` -> `address.city`
57
+ * `/data/relationships/author` -> `author`
58
+ * `/email` -> `email`
59
+ * `/` -> null (root, non-field)
60
+ * `` -> null
61
+ */
62
+ function pointerToField(pointer) {
63
+ if (!pointer || pointer === '/')
64
+ return null;
65
+ const segments = pointer
66
+ .replace(/^\//, '')
67
+ .split('/')
68
+ .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'))
69
+ .filter(Boolean);
70
+ return segments.length > 0 ? resolveJsonApiSegments(segments) : null;
71
+ }
72
+ /** Strip well-known JSON:API structural prefixes and join as dot-path. */
73
+ function resolveJsonApiSegments(segments) {
74
+ if (segments[0] !== 'data')
75
+ return segments.join('.');
76
+ if (segments[1] === 'attributes') {
77
+ const rest = segments.slice(2);
78
+ return rest.length > 0 ? rest.join('.') : null;
79
+ }
80
+ if (segments[1] === 'relationships' && segments.length > 2) {
81
+ return segments.slice(2).join('.');
82
+ }
83
+ return segments.join('.');
84
+ }
85
+ //# sourceMappingURL=json-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-api.js","sourceRoot":"","sources":["../../src/parsers/json-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ParsedErrors } from './types.js';
2
+ /**
3
+ * Laravel / DRF top-level field-to-messages map:
4
+ * { "email": ["must not be blank", "invalid format"], "name": ["required"] }
5
+ *
6
+ * Guard: at least one value must be a non-empty string array, and the body
7
+ * must not use keys owned by other parsers.
8
+ */
9
+ export declare function tryParseLaravelDrf(body: Record<string, unknown>): ParsedErrors | null;
10
+ //# sourceMappingURL=laravel-drf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laravel-drf.d.ts","sourceRoot":"","sources":["../../src/parsers/laravel-drf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,YAAY,CAAA;AAuB1D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,GAAG,IAAI,CAgBrF"}
@@ -0,0 +1,46 @@
1
+ import { isStringArray } from './utils.js';
2
+ /** Keys that belong to other parsers and disqualify a body from Laravel/DRF matching. */
3
+ const LARAVEL_RESERVED_KEYS = new Set([
4
+ 'errors',
5
+ 'violations',
6
+ 'invalid-params',
7
+ 'field',
8
+ 'message',
9
+ 'detail',
10
+ 'title',
11
+ 'status',
12
+ 'type',
13
+ // Zod flatten shape keys — prevent laravel-drf from consuming zod-flatten bodies.
14
+ 'fieldErrors',
15
+ 'formErrors',
16
+ ]);
17
+ function hasReservedLaravelKey(entries) {
18
+ return entries.some(([k]) => LARAVEL_RESERVED_KEYS.has(k));
19
+ }
20
+ /**
21
+ * Laravel / DRF top-level field-to-messages map:
22
+ * { "email": ["must not be blank", "invalid format"], "name": ["required"] }
23
+ *
24
+ * Guard: at least one value must be a non-empty string array, and the body
25
+ * must not use keys owned by other parsers.
26
+ */
27
+ export function tryParseLaravelDrf(body) {
28
+ const entries = Object.entries(body);
29
+ if (entries.length === 0 || hasReservedLaravelKey(entries))
30
+ return null;
31
+ // Require at least one non-empty string array to distinguish from generic flat objects
32
+ if (!entries.some(([, v]) => isStringArray(v) && v.length > 0))
33
+ return null;
34
+ const fieldErrors = [];
35
+ for (const [rawField, messages] of entries) {
36
+ if (isStringArray(messages)) {
37
+ for (const message of messages)
38
+ fieldErrors.push({ field: rawField, message });
39
+ }
40
+ else if (typeof messages === 'string') {
41
+ fieldErrors.push({ field: rawField, message: messages });
42
+ }
43
+ }
44
+ return fieldErrors.length > 0 ? { fieldErrors, formErrors: [] } : null;
45
+ }
46
+ //# sourceMappingURL=laravel-drf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laravel-drf.js","sourceRoot":"","sources":["../../src/parsers/laravel-drf.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,yFAAyF;AACzF,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,QAAQ;IACR,YAAY;IACZ,gBAAgB;IAChB,OAAO;IACP,SAAS;IACT,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,MAAM;IACN,kFAAkF;IAClF,aAAa;IACb,YAAY;CACb,CAAC,CAAA;AAEF,SAAS,qBAAqB,CAAC,OAA4B;IACzD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC9D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,qBAAqB,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvE,uFAAuF;IACvF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAK,CAAc,CAAC,MAAM,GAAG,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAEzF,MAAM,WAAW,GAAiB,EAAE,CAAA;IACpC,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;QAC3C,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ;gBAAE,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;QAChF,CAAC;aAAM,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACxC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACxE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { FieldError } from './types.js';
2
+ /**
3
+ * RFC 7807 Problem Details (Spring Boot 3+ default):
4
+ * { "errors": { "email": ["must not be blank"], "name": ["too short"] } }
5
+ */
6
+ export declare function tryParseRfc7807(body: Record<string, unknown>, transformField: (field: string) => string): FieldError[] | null;
7
+ //# sourceMappingURL=rfc7807.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfc7807.d.ts","sourceRoot":"","sources":["../../src/parsers/rfc7807.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACxC,UAAU,EAAE,GAAG,IAAI,CAerB"}
@@ -0,0 +1,24 @@
1
+ import { isObject, isStringArray } from './utils.js';
2
+ /**
3
+ * RFC 7807 Problem Details (Spring Boot 3+ default):
4
+ * { "errors": { "email": ["must not be blank"], "name": ["too short"] } }
5
+ */
6
+ export function tryParseRfc7807(body, transformField) {
7
+ const { errors } = body;
8
+ if (!isObject(errors))
9
+ return null;
10
+ const result = [];
11
+ for (const [rawField, messages] of Object.entries(errors)) {
12
+ const field = transformField(rawField);
13
+ if (isStringArray(messages)) {
14
+ for (const message of messages)
15
+ result.push({ field, message });
16
+ }
17
+ else if (typeof messages === 'string') {
18
+ result.push({ field, message: messages });
19
+ }
20
+ // null / unknown value shapes are skipped — no partial/garbage errors
21
+ }
22
+ return result.length > 0 ? result : null;
23
+ }
24
+ //# sourceMappingURL=rfc7807.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfc7807.js","sourceRoot":"","sources":["../../src/parsers/rfc7807.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEpD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAA6B,EAC7B,cAAyC;IAEzC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAA;IAElC,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;QACtC,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QACjE,CAAC;aAAM,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC3C,CAAC;QACD,sEAAsE;IACxE,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { FieldError } from './types.js';
2
+ /**
3
+ * Spring Boot default validation format (pre-3):
4
+ * { "errors": [{ "field": "email", "defaultMessage": "must not be blank" }] }
5
+ */
6
+ export declare function tryParseSpringArray(body: Record<string, unknown>, fallbackField: string, transformField: (field: string) => string): FieldError[] | null;
7
+ //# sourceMappingURL=spring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring.d.ts","sourceRoot":"","sources":["../../src/parsers/spring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACxC,UAAU,EAAE,GAAG,IAAI,CAiBrB"}
@@ -0,0 +1,24 @@
1
+ import { isObject } from './utils.js';
2
+ /**
3
+ * Spring Boot default validation format (pre-3):
4
+ * { "errors": [{ "field": "email", "defaultMessage": "must not be blank" }] }
5
+ */
6
+ export function tryParseSpringArray(body, fallbackField, transformField) {
7
+ const { errors } = body;
8
+ if (!Array.isArray(errors))
9
+ return null;
10
+ const result = [];
11
+ for (const item of errors) {
12
+ if (!isObject(item))
13
+ continue;
14
+ const rawField = typeof item['field'] === 'string' ? item['field'] : fallbackField;
15
+ const message = typeof item['defaultMessage'] === 'string'
16
+ ? item['defaultMessage']
17
+ : typeof item['message'] === 'string'
18
+ ? item['message']
19
+ : 'Unknown error';
20
+ result.push({ field: transformField(rawField), message });
21
+ }
22
+ return result.length > 0 ? result : null;
23
+ }
24
+ //# sourceMappingURL=spring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring.js","sourceRoot":"","sources":["../../src/parsers/spring.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAA6B,EAC7B,aAAqB,EACrB,cAAyC;IAEzC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvC,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC7B,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAA;QAClF,MAAM,OAAO,GACX,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,QAAQ;YACxC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;YACxB,CAAC,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ;gBACnC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;gBACjB,CAAC,CAAC,eAAe,CAAA;QACvB,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Normalized representation of a single field-level error.
3
+ */
4
+ export interface FieldError {
5
+ field: string;
6
+ message: string;
7
+ }
8
+ /**
9
+ * Raw parse result used internally and returned by custom parsers.
10
+ * Both arrays may be empty if the shape was recognized but contained no errors.
11
+ */
12
+ export interface ParsedErrors {
13
+ fieldErrors: FieldError[];
14
+ formErrors: string[];
15
+ }
16
+ /**
17
+ * The format/shape that was recognized when parsing an error body.
18
+ *
19
+ * - `'rfc7807-map'` - RFC 7807 / RFC 9457 `errors` map: `{ errors: { field: [...] } }`
20
+ * - `'spring-array'` - Spring Boot array: `{ errors: [{ field, defaultMessage }] }`
21
+ * - `'violations'` - RFC 9457 / Spring `violations` array: `{ violations: [{ field, message }] }`
22
+ * - `'invalid-params'` - RFC 9457 `invalid-params` array: `{ "invalid-params": [{ name, reason }] }`
23
+ * - `'json-api'` - JSON:API errors array: `{ errors: [{ source: { pointer }, detail }] }`
24
+ * - `'graphql-extensions'` - GraphQL errors array: `{ errors: [{ message, extensions?: { field?, path? } }] }`
25
+ * - `'zod-flatten'` - Zod flatten shape: `{ fieldErrors: { field: [...] }, formErrors: [...] }`
26
+ * - `'laravel-drf'` - Laravel / DRF top-level field map: `{ field: ["msg1", ...] }`
27
+ * - `'flat-object'` - Simple flat object: `{ field, message }`
28
+ * - `'flat-array'` - Array of flat objects: `[{ field, message }]`
29
+ * - `'rfc9457-detail'` - RFC 9457 top-level `detail` string (last-resort fallback)
30
+ * - `'custom'` - Matched a caller-provided custom parser
31
+ */
32
+ export type ErrorFormat = 'rfc7807-map' | 'spring-array' | 'violations' | 'invalid-params' | 'json-api' | 'graphql-extensions' | 'zod-flatten' | 'laravel-drf' | 'flat-object' | 'flat-array' | 'rfc9457-detail' | 'custom';
33
+ /**
34
+ * A custom parser that callers can register to handle application-specific
35
+ * error shapes not covered by the built-in parsers.
36
+ *
37
+ * Return `null` to indicate "not recognized" and let the next parser try.
38
+ * Return a `ParsedErrors` object (even if both arrays are empty) to signal
39
+ * that this shape was recognized.
40
+ */
41
+ export interface CustomParser {
42
+ (body: unknown): ParsedErrors | null;
43
+ }
44
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/parsers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,GACnB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,UAAU,GACV,oBAAoB,GACpB,aAAa,GACb,aAAa,GACb,aAAa,GACb,YAAY,GACZ,gBAAgB,GAChB,QAAQ,CAAA;AAEZ;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAAA;CACrC"}