@orsetra/shared-ui 1.0.24 → 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.
Files changed (2) hide show
  1. package/lib/http-client.ts +44 -2
  2. package/package.json +2 -2
@@ -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;
@@ -58,8 +74,9 @@ class HttpClient {
58
74
  if (!response.ok) {
59
75
  // Try to extract error message from API response
60
76
  let errorMessage = `Request failed: ${response.status}`;
77
+ let errorBody: any = undefined;
61
78
  try {
62
- const errorBody = await response.json();
79
+ errorBody = await response.json();
63
80
  // Zitadel API returns { code, message, details } on error
64
81
  if (errorBody?.message) {
65
82
  errorMessage = errorBody.message;
@@ -69,7 +86,32 @@ class HttpClient {
69
86
  } catch {
70
87
  // If we can't parse JSON, use the default message
71
88
  }
72
- throw new Error(errorMessage);
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;
73
115
  }
74
116
 
75
117
  if (response.status === 204) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.0.24",
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
+ }