@orsetra/shared-ui 1.0.23 → 1.0.25
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/lib/http-client.ts +84 -4
- package/package.json +2 -2
package/lib/http-client.ts
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
export type AuthHeadersProvider = () => Promise<Record<string, string>> | Record<string, string>;
|
|
4
4
|
|
|
5
|
+
export class ApiError extends Error {
|
|
6
|
+
status: number;
|
|
7
|
+
code?: number;
|
|
8
|
+
details?: unknown;
|
|
9
|
+
raw?: unknown;
|
|
10
|
+
|
|
11
|
+
constructor(message: string, params: { status: number; code?: number; details?: unknown; raw?: unknown }) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'ApiError';
|
|
14
|
+
this.status = params.status;
|
|
15
|
+
this.code = params.code;
|
|
16
|
+
this.details = params.details;
|
|
17
|
+
this.raw = params.raw;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
class HttpClient {
|
|
6
22
|
private baseUrl: string;
|
|
7
23
|
private authHeadersProvider?: AuthHeadersProvider;
|
|
@@ -56,7 +72,46 @@ class HttpClient {
|
|
|
56
72
|
});
|
|
57
73
|
|
|
58
74
|
if (!response.ok) {
|
|
59
|
-
|
|
75
|
+
// Try to extract error message from API response
|
|
76
|
+
let errorMessage = `Request failed: ${response.status}`;
|
|
77
|
+
let errorBody: any = undefined;
|
|
78
|
+
try {
|
|
79
|
+
errorBody = await response.json();
|
|
80
|
+
// Zitadel API returns { code, message, details } on error
|
|
81
|
+
if (errorBody?.message) {
|
|
82
|
+
errorMessage = errorBody.message;
|
|
83
|
+
} else if (errorBody?.error) {
|
|
84
|
+
errorMessage = errorBody.error;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// If we can't parse JSON, use the default message
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const apiError = new ApiError(errorMessage, {
|
|
91
|
+
status: response.status,
|
|
92
|
+
code: typeof errorBody?.code === 'number' ? errorBody.code : undefined,
|
|
93
|
+
details: errorBody?.details,
|
|
94
|
+
raw: errorBody,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Zitadel step-up requirement
|
|
98
|
+
const isMfaRequired =
|
|
99
|
+
apiError.code === 7 ||
|
|
100
|
+
(typeof errorMessage === 'string' && errorMessage.toLowerCase().includes('mfa required'));
|
|
101
|
+
|
|
102
|
+
if (isMfaRequired && typeof window !== 'undefined') {
|
|
103
|
+
try {
|
|
104
|
+
window.dispatchEvent(
|
|
105
|
+
new CustomEvent('zitadel:mfa-required', {
|
|
106
|
+
detail: { url, status: apiError.status, code: apiError.code, error: apiError.raw },
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
} catch {
|
|
110
|
+
// ignore event dispatch issues
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
throw apiError;
|
|
60
115
|
}
|
|
61
116
|
|
|
62
117
|
if (response.status === 204) {
|
|
@@ -121,8 +176,23 @@ class HttpClient {
|
|
|
121
176
|
});
|
|
122
177
|
|
|
123
178
|
if (!response.ok) {
|
|
124
|
-
|
|
125
|
-
|
|
179
|
+
// Try to extract error message from API response
|
|
180
|
+
let errorMessage = `Upload failed: ${response.status} ${response.statusText}`;
|
|
181
|
+
try {
|
|
182
|
+
const errorBody = await response.json();
|
|
183
|
+
if (errorBody?.message) {
|
|
184
|
+
errorMessage = errorBody.message;
|
|
185
|
+
} else if (errorBody?.error) {
|
|
186
|
+
errorMessage = errorBody.error;
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
// Try text if JSON fails
|
|
190
|
+
try {
|
|
191
|
+
const errorText = await response.text();
|
|
192
|
+
if (errorText) errorMessage = errorText;
|
|
193
|
+
} catch { /* ignore */ }
|
|
194
|
+
}
|
|
195
|
+
throw new Error(errorMessage);
|
|
126
196
|
}
|
|
127
197
|
|
|
128
198
|
if (response.status === 204) {
|
|
@@ -143,7 +213,17 @@ class HttpClient {
|
|
|
143
213
|
});
|
|
144
214
|
|
|
145
215
|
if (!response.ok) {
|
|
146
|
-
|
|
216
|
+
// Try to extract error message from API response
|
|
217
|
+
let errorMessage = `Download failed: ${response.status} ${response.statusText}`;
|
|
218
|
+
try {
|
|
219
|
+
const errorBody = await response.json();
|
|
220
|
+
if (errorBody?.message) {
|
|
221
|
+
errorMessage = errorBody.message;
|
|
222
|
+
} else if (errorBody?.error) {
|
|
223
|
+
errorMessage = errorBody.error;
|
|
224
|
+
}
|
|
225
|
+
} catch { /* ignore */ }
|
|
226
|
+
throw new Error(errorMessage);
|
|
147
227
|
}
|
|
148
228
|
|
|
149
229
|
return await response.blob();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orsetra/shared-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "Shared UI components for Orsetra platform",
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"types": "./index.ts",
|
|
@@ -92,4 +92,4 @@
|
|
|
92
92
|
"next": "^16.0.7",
|
|
93
93
|
"typescript": "^5"
|
|
94
94
|
}
|
|
95
|
-
}
|
|
95
|
+
}
|