@next-feature/client 0.1.0-beta → 0.1.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/.babelrc +12 -12
- package/README.md +7 -518
- package/eslint.config.mjs +12 -12
- package/{jest.config.ts → jest.config.cts} +10 -10
- package/package.json +6 -4
- package/project.json +32 -32
- package/src/components/api-error-boundary.tsx +58 -58
- package/src/hooks/use-api-error.tsx +39 -39
- package/src/index.ts +30 -6
- package/src/lib/client.ts +431 -431
- package/src/lib/error.ts +169 -166
- package/src/lib/types/index.ts +13 -13
- package/src/lib/utils/error.ts +136 -136
- package/src/lib/utils/helper.ts +20 -20
- package/tsconfig.json +20 -20
- package/tsconfig.lib.json +29 -28
- package/tsconfig.spec.json +23 -22
- package/vite.config.ts +51 -48
package/src/lib/error.ts
CHANGED
|
@@ -1,166 +1,169 @@
|
|
|
1
|
-
import { AxiosError, HttpStatusCode } from 'axios';
|
|
2
|
-
import { ZodError } from 'zod';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
public
|
|
24
|
-
public
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
problemDetail?.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this
|
|
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
|
-
private
|
|
96
|
-
private
|
|
97
|
-
private
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
1
|
+
import { AxiosError, HttpStatusCode } from 'axios';
|
|
2
|
+
import { ZodError } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Spring Boot ProblemDetail structure
|
|
6
|
+
*/
|
|
7
|
+
export interface ProblemDetail {
|
|
8
|
+
type: string;
|
|
9
|
+
title: string;
|
|
10
|
+
status: HttpStatusCode;
|
|
11
|
+
detail?: string;
|
|
12
|
+
instance?: string;
|
|
13
|
+
errors?: Record<string, unknown>;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Custom error class for API errors
|
|
19
|
+
*/
|
|
20
|
+
export class ApiError extends Error {
|
|
21
|
+
constructor(
|
|
22
|
+
public status: HttpStatusCode,
|
|
23
|
+
public problemDetail: ProblemDetail | null,
|
|
24
|
+
public originalError?: Error,
|
|
25
|
+
message?: string
|
|
26
|
+
) {
|
|
27
|
+
super(
|
|
28
|
+
message ||
|
|
29
|
+
problemDetail?.detail ||
|
|
30
|
+
problemDetail?.title ||
|
|
31
|
+
originalError?.message ||
|
|
32
|
+
'An error occurred'
|
|
33
|
+
);
|
|
34
|
+
this.name = 'ApiError';
|
|
35
|
+
Object.setPrototypeOf(this, ApiError.prototype);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get isClientError(): boolean {
|
|
39
|
+
return this.status >= 400 && this.status < 500;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get isServerError(): boolean {
|
|
43
|
+
return this.status >= 500;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get isUnauthorized(): boolean {
|
|
47
|
+
return this.status === 401;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get isForbidden(): boolean {
|
|
51
|
+
return this.status === 403;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get isNotFound(): boolean {
|
|
55
|
+
return this.status === 404;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static builder<T>(): ApiErrorBuilder {
|
|
59
|
+
return new ApiErrorBuilder<T>();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static of(error: Error): ApiError {
|
|
63
|
+
if (error instanceof ApiError) {
|
|
64
|
+
return error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return ApiError.builder().originalError(error).build();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create ApiError from Zod validation error
|
|
72
|
+
*/
|
|
73
|
+
static fromZodError(zodError: ZodError) {
|
|
74
|
+
const errors: Record<string, string> = {};
|
|
75
|
+
|
|
76
|
+
zodError.errors.forEach((error) => {
|
|
77
|
+
error.path.forEach((path) => {
|
|
78
|
+
errors[path] = error.message;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return ApiError.builder()
|
|
83
|
+
.originalError(zodError)
|
|
84
|
+
.status(HttpStatusCode.BadRequest)
|
|
85
|
+
.message('Validation error')
|
|
86
|
+
.detail('errors', errors)
|
|
87
|
+
.build();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class ApiErrorBuilder<
|
|
92
|
+
AdditionalProblemDetails = Record<string, unknown>
|
|
93
|
+
> {
|
|
94
|
+
private _problemDetail: ProblemDetail;
|
|
95
|
+
private _status: HttpStatusCode;
|
|
96
|
+
private _originalError: Error;
|
|
97
|
+
private _message: string;
|
|
98
|
+
|
|
99
|
+
constructor() {
|
|
100
|
+
this._status = HttpStatusCode.InternalServerError;
|
|
101
|
+
this._problemDetail = {
|
|
102
|
+
status: this._status,
|
|
103
|
+
title: '',
|
|
104
|
+
type: 'about:blank',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Set standard ProblemDetail fields
|
|
110
|
+
*/
|
|
111
|
+
detail<K extends keyof (ProblemDetail & AdditionalProblemDetails)>(
|
|
112
|
+
key: K,
|
|
113
|
+
value: K extends keyof ProblemDetail
|
|
114
|
+
? ProblemDetail[K]
|
|
115
|
+
: K extends keyof AdditionalProblemDetails
|
|
116
|
+
? AdditionalProblemDetails[K]
|
|
117
|
+
: unknown
|
|
118
|
+
): ApiErrorBuilder<AdditionalProblemDetails> {
|
|
119
|
+
(this._problemDetail as any)[key] = value;
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Set standard ProblemDetail fields
|
|
125
|
+
*/
|
|
126
|
+
problemDetail(
|
|
127
|
+
problemDetail: ProblemDetail
|
|
128
|
+
): ApiErrorBuilder<AdditionalProblemDetails> {
|
|
129
|
+
if (problemDetail) {
|
|
130
|
+
this._problemDetail = problemDetail;
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
originalError(error: Error): ApiErrorBuilder<AdditionalProblemDetails> {
|
|
136
|
+
this._originalError = error;
|
|
137
|
+
this._problemDetail.title = error.name;
|
|
138
|
+
|
|
139
|
+
if (error instanceof AxiosError) {
|
|
140
|
+
this.status(error.status);
|
|
141
|
+
}
|
|
142
|
+
if (error instanceof ZodError) {
|
|
143
|
+
this.status(HttpStatusCode.BadRequest);
|
|
144
|
+
this.message('Validation error');
|
|
145
|
+
}
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
status(status: HttpStatusCode): ApiErrorBuilder<AdditionalProblemDetails> {
|
|
150
|
+
this._status = status;
|
|
151
|
+
this._problemDetail.status = status;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
message(msg: string): ApiErrorBuilder<AdditionalProblemDetails> {
|
|
156
|
+
this._message = msg;
|
|
157
|
+
this._problemDetail.detail = msg;
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
build(): ApiError {
|
|
162
|
+
return new ApiError(
|
|
163
|
+
this._status,
|
|
164
|
+
this._problemDetail,
|
|
165
|
+
this._originalError,
|
|
166
|
+
this._message
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/lib/types/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* [api-response]
|
|
5
|
-
* next-feature@0.0.11-beta
|
|
6
|
-
* November 4th 2025, 6:37:27 pm
|
|
7
|
-
*/
|
|
8
|
-
export interface ApiResponse<Response> {
|
|
9
|
-
success?: boolean;
|
|
10
|
-
message?: string;
|
|
11
|
-
error?:
|
|
12
|
-
data: Response;
|
|
13
|
-
}
|
|
1
|
+
import { ProblemDetail } from '../error';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* [api-response]
|
|
5
|
+
* next-feature@0.0.11-beta
|
|
6
|
+
* November 4th 2025, 6:37:27 pm
|
|
7
|
+
*/
|
|
8
|
+
export interface ApiResponse<Response> {
|
|
9
|
+
success?: boolean;
|
|
10
|
+
message?: string;
|
|
11
|
+
error?: ProblemDetail;
|
|
12
|
+
data: Response;
|
|
13
|
+
}
|
package/src/lib/utils/error.ts
CHANGED
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
import { ApiError } from '../error';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extract user-friendly error message from ApiError
|
|
5
|
-
*
|
|
6
|
-
* [get-error-message]
|
|
7
|
-
* next-feature@0.0.11-beta
|
|
8
|
-
* November 4th 2025, 11:47:45 am
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
export function getErrorMessage(error: unknown): string {
|
|
12
|
-
if (error instanceof ApiError) {
|
|
13
|
-
// Use Spring Boot ProblemDetail information
|
|
14
|
-
if (error.problemDetail?.detail) {
|
|
15
|
-
return error.problemDetail.detail;
|
|
16
|
-
}
|
|
17
|
-
if (error.problemDetail?.title) {
|
|
18
|
-
return error.problemDetail.title;
|
|
19
|
-
}
|
|
20
|
-
return error.message;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (error instanceof Error) {
|
|
24
|
-
return error.message;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return 'An unexpected error occurred';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Format ProblemDetail for display
|
|
32
|
-
*/
|
|
33
|
-
export function formatProblemDetail(error: ApiError): string {
|
|
34
|
-
if (!error.problemDetail) {
|
|
35
|
-
return error.message;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const parts: string[] = [];
|
|
39
|
-
|
|
40
|
-
if (error.problemDetail.title) {
|
|
41
|
-
parts.push(`Title: ${error.problemDetail.title}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (error.problemDetail.detail) {
|
|
45
|
-
parts.push(`Detail: ${error.problemDetail.detail}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (error.problemDetail.instance) {
|
|
49
|
-
parts.push(`Instance: ${error.problemDetail.instance}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return parts.join('\n');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if error is a specific HTTP status
|
|
57
|
-
*/
|
|
58
|
-
export function isHttpStatus(error: unknown, status: number): boolean {
|
|
59
|
-
return error instanceof ApiError && error.status === status;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Handle common API errors
|
|
64
|
-
*/
|
|
65
|
-
export function handleApiError(error: unknown): void {
|
|
66
|
-
if (!(error instanceof ApiError)) {
|
|
67
|
-
console.error('Unexpected error:', error);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
switch (error.status) {
|
|
72
|
-
case 400:
|
|
73
|
-
console.error('Bad Request:', error.problemDetail?.detail);
|
|
74
|
-
break;
|
|
75
|
-
case 401:
|
|
76
|
-
console.error('Unauthorized - Please login');
|
|
77
|
-
break;
|
|
78
|
-
case 403:
|
|
79
|
-
console.error('Forbidden - Access denied');
|
|
80
|
-
break;
|
|
81
|
-
case 404:
|
|
82
|
-
console.error('Not Found:', error.problemDetail?.detail);
|
|
83
|
-
break;
|
|
84
|
-
case 409:
|
|
85
|
-
console.error('Conflict:', error.problemDetail?.detail);
|
|
86
|
-
break;
|
|
87
|
-
case 422:
|
|
88
|
-
console.error('Validation Error:', error.problemDetail);
|
|
89
|
-
break;
|
|
90
|
-
case 429:
|
|
91
|
-
console.error('Too Many Requests - Please slow down');
|
|
92
|
-
break;
|
|
93
|
-
case 500:
|
|
94
|
-
console.error('Server Error:', error.problemDetail?.detail);
|
|
95
|
-
break;
|
|
96
|
-
case 503:
|
|
97
|
-
console.error('Service Unavailable - Please try again later');
|
|
98
|
-
break;
|
|
99
|
-
default:
|
|
100
|
-
console.error(`Error ${error.status}:`, error.message);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Validation error extractor for Spring Boot validation errors
|
|
106
|
-
*/
|
|
107
|
-
export function extractValidationErrors(
|
|
108
|
-
error: ApiError
|
|
109
|
-
): Record<string, string[]> | null {
|
|
110
|
-
if (!error.problemDetail || error.status !== 400) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Spring Boot often includes validation errors in a 'errors' or 'fieldErrors' property
|
|
115
|
-
const errors = error.problemDetail.errors || error.problemDetail.fieldErrors;
|
|
116
|
-
|
|
117
|
-
if (!errors) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Convert to a more usable format
|
|
122
|
-
if (Array.isArray(errors)) {
|
|
123
|
-
const result: Record<string, string[]> = {};
|
|
124
|
-
errors.forEach((err: any) => {
|
|
125
|
-
const field = err.field || err.property || 'general';
|
|
126
|
-
const message = err.message || err.defaultMessage || 'Validation error';
|
|
127
|
-
if (!result[field]) {
|
|
128
|
-
result[field] = [];
|
|
129
|
-
}
|
|
130
|
-
result[field].push(message);
|
|
131
|
-
});
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return errors as Record<string, string[]>;
|
|
136
|
-
}
|
|
1
|
+
import { ApiError } from '../error';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract user-friendly error message from ApiError
|
|
5
|
+
*
|
|
6
|
+
* [get-error-message]
|
|
7
|
+
* next-feature@0.0.11-beta
|
|
8
|
+
* November 4th 2025, 11:47:45 am
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export function getErrorMessage(error: unknown): string {
|
|
12
|
+
if (error instanceof ApiError) {
|
|
13
|
+
// Use Spring Boot ProblemDetail information
|
|
14
|
+
if (error.problemDetail?.detail) {
|
|
15
|
+
return error.problemDetail.detail;
|
|
16
|
+
}
|
|
17
|
+
if (error.problemDetail?.title) {
|
|
18
|
+
return error.problemDetail.title;
|
|
19
|
+
}
|
|
20
|
+
return error.message;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (error instanceof Error) {
|
|
24
|
+
return error.message;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return 'An unexpected error occurred';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Format ProblemDetail for display
|
|
32
|
+
*/
|
|
33
|
+
export function formatProblemDetail(error: ApiError): string {
|
|
34
|
+
if (!error.problemDetail) {
|
|
35
|
+
return error.message;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const parts: string[] = [];
|
|
39
|
+
|
|
40
|
+
if (error.problemDetail.title) {
|
|
41
|
+
parts.push(`Title: ${error.problemDetail.title}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (error.problemDetail.detail) {
|
|
45
|
+
parts.push(`Detail: ${error.problemDetail.detail}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (error.problemDetail.instance) {
|
|
49
|
+
parts.push(`Instance: ${error.problemDetail.instance}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return parts.join('\n');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if error is a specific HTTP status
|
|
57
|
+
*/
|
|
58
|
+
export function isHttpStatus(error: unknown, status: number): boolean {
|
|
59
|
+
return error instanceof ApiError && error.status === status;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle common API errors
|
|
64
|
+
*/
|
|
65
|
+
export function handleApiError(error: unknown): void {
|
|
66
|
+
if (!(error instanceof ApiError)) {
|
|
67
|
+
console.error('Unexpected error:', error);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
switch (error.status) {
|
|
72
|
+
case 400:
|
|
73
|
+
console.error('Bad Request:', error.problemDetail?.detail);
|
|
74
|
+
break;
|
|
75
|
+
case 401:
|
|
76
|
+
console.error('Unauthorized - Please login');
|
|
77
|
+
break;
|
|
78
|
+
case 403:
|
|
79
|
+
console.error('Forbidden - Access denied');
|
|
80
|
+
break;
|
|
81
|
+
case 404:
|
|
82
|
+
console.error('Not Found:', error.problemDetail?.detail);
|
|
83
|
+
break;
|
|
84
|
+
case 409:
|
|
85
|
+
console.error('Conflict:', error.problemDetail?.detail);
|
|
86
|
+
break;
|
|
87
|
+
case 422:
|
|
88
|
+
console.error('Validation Error:', error.problemDetail);
|
|
89
|
+
break;
|
|
90
|
+
case 429:
|
|
91
|
+
console.error('Too Many Requests - Please slow down');
|
|
92
|
+
break;
|
|
93
|
+
case 500:
|
|
94
|
+
console.error('Server Error:', error.problemDetail?.detail);
|
|
95
|
+
break;
|
|
96
|
+
case 503:
|
|
97
|
+
console.error('Service Unavailable - Please try again later');
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
console.error(`Error ${error.status}:`, error.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Validation error extractor for Spring Boot validation errors
|
|
106
|
+
*/
|
|
107
|
+
export function extractValidationErrors(
|
|
108
|
+
error: ApiError
|
|
109
|
+
): Record<string, string[]> | null {
|
|
110
|
+
if (!error.problemDetail || error.status !== 400) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Spring Boot often includes validation errors in a 'errors' or 'fieldErrors' property
|
|
115
|
+
const errors = error.problemDetail.errors || error.problemDetail.fieldErrors;
|
|
116
|
+
|
|
117
|
+
if (!errors) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Convert to a more usable format
|
|
122
|
+
if (Array.isArray(errors)) {
|
|
123
|
+
const result: Record<string, string[]> = {};
|
|
124
|
+
errors.forEach((err: any) => {
|
|
125
|
+
const field = err.field || err.property || 'general';
|
|
126
|
+
const message = err.message || err.defaultMessage || 'Validation error';
|
|
127
|
+
if (!result[field]) {
|
|
128
|
+
result[field] = [];
|
|
129
|
+
}
|
|
130
|
+
result[field].push(message);
|
|
131
|
+
});
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return errors as Record<string, string[]>;
|
|
136
|
+
}
|
package/src/lib/utils/helper.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { ProblemDetail } from '../error';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* [show-error-toast]
|
|
5
|
-
* next-feature@0.0.11-beta
|
|
6
|
-
* November 4th 2025, 11:58:52 am
|
|
7
|
-
*/
|
|
8
|
-
export function showErrorToast(data: any) {
|
|
9
|
-
return data;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getFieldError(
|
|
13
|
-
error: ProblemDetail | undefined,
|
|
14
|
-
fieldName: string
|
|
15
|
-
): string | undefined {
|
|
16
|
-
if (!error) return undefined;
|
|
17
|
-
|
|
18
|
-
const errors = error.errors as Record<string, string>;
|
|
19
|
-
return errors?.[fieldName];
|
|
20
|
-
}
|
|
1
|
+
import { ProblemDetail } from '../error';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* [show-error-toast]
|
|
5
|
+
* next-feature@0.0.11-beta
|
|
6
|
+
* November 4th 2025, 11:58:52 am
|
|
7
|
+
*/
|
|
8
|
+
export function showErrorToast(data: any) {
|
|
9
|
+
return data;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getFieldError(
|
|
13
|
+
error: ProblemDetail | undefined,
|
|
14
|
+
fieldName: string
|
|
15
|
+
): string | undefined {
|
|
16
|
+
if (!error) return undefined;
|
|
17
|
+
|
|
18
|
+
const errors = error.errors as Record<string, string>;
|
|
19
|
+
return errors?.[fieldName];
|
|
20
|
+
}
|