@zerosls/clm-sdk 1.1.6 → 1.1.8

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.
@@ -3,6 +3,7 @@ import { RequestOptions } from "../types/common";
3
3
  import { EventEmitter } from "./event-emitter";
4
4
  export declare class ApiClient {
5
5
  private baseUrl;
6
+ private fallbackBaseUrl;
6
7
  private organization;
7
8
  private token;
8
9
  private eventEmitter;
@@ -6,6 +6,8 @@ export class ApiClient {
6
6
  var _a, _b;
7
7
  this.token = null;
8
8
  this.baseUrl = config.baseUrl;
9
+ this.fallbackBaseUrl =
10
+ config.fallbackBaseUrl || "http://216.250.117.119/ZeroServicesQA/api/v1";
9
11
  this.organization = config.organization;
10
12
  this.token = config.token || null;
11
13
  this.eventEmitter = eventEmitter;
@@ -55,16 +57,194 @@ export class ApiClient {
55
57
  }
56
58
  }
57
59
  }
60
+ /*private async request<T>(
61
+ method: string,
62
+ endpoint: string,
63
+ data?: any,
64
+ params?: Record<string, any>,
65
+ options: RequestOptions = {}
66
+ ): Promise<T> {
67
+ // ✅ Primer intento con baseUrl normal
68
+ const primaryUrl = buildUrl(this.baseUrl, endpoint, params);
69
+
70
+ const base: HeadersInit = buildHeaders(this.token, {
71
+ "X-Organization": this.organization,
72
+ ...(options.headers || {}),
73
+ });
74
+
75
+ const headers = new Headers(base);
76
+
77
+ // ✅ Obtener token legacy
78
+ const legacyToken =
79
+ (window as any).__LEGACY_TOKEN__ ||
80
+ sessionStorage.getItem("legacy_token") ||
81
+ null;
82
+
83
+ if (legacyToken) {
84
+ headers.set("Authorization", `Bearer ${legacyToken}`);
85
+ if (this.debug) {
86
+ console.log("🔐 Using legacy token for:", endpoint);
87
+ }
88
+ } else if (this.token) {
89
+ if (this.debug) {
90
+ console.log("🔐 Using v1 token for:", endpoint);
91
+ }
92
+ } else {
93
+ console.warn("⚠️ No token available for endpoint:", endpoint);
94
+ }
95
+
96
+ const useCache = this.cacheEnabled && options.useCache !== false;
97
+ if (useCache && method === "GET") {
98
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
99
+ const cachedData = this.cache.get<T>(cacheKey);
100
+ if (cachedData) {
101
+ if (this.debug) {
102
+ console.log(`[SDK-Cache] Hit: ${cacheKey}`);
103
+ }
104
+ return cachedData;
105
+ }
106
+ }
107
+
108
+ this.eventEmitter.emit("beforeRequest", {
109
+ url: primaryUrl,
110
+ method,
111
+ data,
112
+ });
113
+
114
+ try {
115
+ const fetchOptions: RequestInit = {
116
+ method,
117
+ headers,
118
+ credentials: "include",
119
+ };
120
+
121
+ if (data && method !== "GET") {
122
+ fetchOptions.body = JSON.stringify(data);
123
+ if (this.debug) {
124
+ console.log(`📤 ${method} Body:`, data);
125
+ }
126
+ }
127
+
128
+ if (this.debug) {
129
+ console.log(`🌐 ${method} ${primaryUrl}`);
130
+ }
131
+
132
+ // ✅ Primer intento
133
+ const response = await fetch(primaryUrl, fetchOptions);
134
+
135
+ // ✅ Si es 404, intentar con fallback URL
136
+ if (response.status === 404) {
137
+ console.warn(`⚠️ 404 en ${primaryUrl}, intentando con fallback...`);
138
+
139
+ const fallbackUrl = buildUrl(this.fallbackBaseUrl, endpoint, params);
140
+
141
+ if (this.debug) {
142
+ console.log(`🔄 Retry: ${method} ${fallbackUrl}`);
143
+ }
144
+
145
+ // ✅ Segundo intento con fallback
146
+ const fallbackResponse = await fetch(fallbackUrl, fetchOptions);
147
+
148
+ if (!fallbackResponse.ok) {
149
+ let errorData;
150
+ try {
151
+ errorData = await fallbackResponse.json();
152
+ } catch {
153
+ errorData = { message: fallbackResponse.statusText };
154
+ }
155
+
156
+ console.error(`❌ Fallback también falló ${fallbackResponse.status}:`, errorData);
157
+
158
+ if (fallbackResponse.status === 401) {
159
+ this.eventEmitter.emit("authError", {
160
+ statusCode: 401,
161
+ message: errorData.message || "Authentication required",
162
+ });
163
+ }
164
+
165
+ return errorData as T;
166
+ }
167
+
168
+ // ✅ Fallback exitoso
169
+ const fallbackData = await parseResponse<T>(fallbackResponse);
170
+
171
+ console.log(`✅ Fallback exitoso para: ${endpoint}`);
172
+
173
+ this.eventEmitter.emit("afterRequest", {
174
+ url: fallbackUrl,
175
+ method,
176
+ response: fallbackData,
177
+ });
178
+
179
+ return fallbackData;
180
+ }
181
+
182
+ // ✅ Otros errores (no 404)
183
+ if (!response.ok) {
184
+ let errorData;
185
+ try {
186
+ errorData = await response.json();
187
+ } catch {
188
+ errorData = { message: response.statusText };
189
+ }
190
+
191
+ console.error(`❌ ${method} ${response.status}:`, errorData);
192
+
193
+ if (response.status === 401) {
194
+ this.eventEmitter.emit("authError", {
195
+ statusCode: 401,
196
+ message: errorData.message || "Authentication required",
197
+ });
198
+ }
199
+
200
+ return errorData as T;
201
+ }
202
+
203
+ // ✅ Respuesta exitosa del primer intento
204
+ const responseData = await parseResponse<T>(response);
205
+
206
+ if (useCache && method === "GET") {
207
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
208
+ const cacheTime = options.cacheTime || undefined;
209
+ this.cache.set(cacheKey, responseData, cacheTime);
210
+
211
+ if (this.debug) {
212
+ console.log(`[SDK-Cache] Set: ${cacheKey}`);
213
+ }
214
+ }
215
+
216
+ this.eventEmitter.emit("afterRequest", {
217
+ url: primaryUrl,
218
+ method,
219
+ response: responseData,
220
+ });
221
+
222
+ return responseData;
223
+ } catch (error) {
224
+ this.eventEmitter.emit("requestError", {
225
+ url: primaryUrl,
226
+ method,
227
+ error,
228
+ });
229
+
230
+ if (error instanceof ApiError) {
231
+ throw error;
232
+ }
233
+
234
+ throw new ApiError((error as Error).message || "Network error", 0, {
235
+ originalError: error,
236
+ });
237
+ }
238
+ }*/
239
+ // core/api-client.ts
58
240
  async request(method, endpoint, data, params, options = {}) {
59
- const url = buildUrl(this.baseUrl, endpoint, params);
241
+ const primaryUrl = buildUrl(this.baseUrl, endpoint, params);
60
242
  const base = buildHeaders(this.token, {
61
243
  "X-Organization": this.organization,
62
244
  ...(options.headers || {}),
63
245
  });
64
246
  const headers = new Headers(base);
65
- //const legacyPattern = /(^|\/)legacy(\/|$)/i;
66
- //const isLegacyEndpoint = legacyPattern.test(endpoint);
67
- //if (isLegacyEndpoint) {
247
+ // Obtener token legacy
68
248
  const legacyToken = window.__LEGACY_TOKEN__ ||
69
249
  sessionStorage.getItem("legacy_token") ||
70
250
  null;
@@ -75,7 +255,6 @@ export class ApiClient {
75
255
  }
76
256
  }
77
257
  else if (this.token) {
78
- // ✅ Si no hay legacy token, usar token v1
79
258
  if (this.debug) {
80
259
  console.log("🔐 Using v1 token for:", endpoint);
81
260
  }
@@ -83,14 +262,9 @@ export class ApiClient {
83
262
  else {
84
263
  console.warn("⚠️ No token available for endpoint:", endpoint);
85
264
  }
86
- //} else {
87
- // if (!this.token) {
88
- // headers.delete("Authorization");
89
- // }
90
- //}
91
265
  const useCache = this.cacheEnabled && options.useCache !== false;
92
266
  if (useCache && method === "GET") {
93
- const cacheKey = generateCacheKey(method, url, data);
267
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
94
268
  const cachedData = this.cache.get(cacheKey);
95
269
  if (cachedData) {
96
270
  if (this.debug) {
@@ -100,7 +274,7 @@ export class ApiClient {
100
274
  }
101
275
  }
102
276
  this.eventEmitter.emit("beforeRequest", {
103
- url,
277
+ url: primaryUrl,
104
278
  method,
105
279
  data,
106
280
  });
@@ -112,16 +286,68 @@ export class ApiClient {
112
286
  };
113
287
  if (data && method !== "GET") {
114
288
  fetchOptions.body = JSON.stringify(data);
115
- console.log(`📤 ${method} Body:`, data);
289
+ if (this.debug) {
290
+ console.log(`📤 ${method} Body:`, data);
291
+ }
292
+ }
293
+ if (this.debug) {
294
+ console.log(`🌐 ${method} ${primaryUrl}`);
295
+ }
296
+ // ✅ Primer intento
297
+ const response = await fetch(primaryUrl, fetchOptions);
298
+ // ✅ Si es 404, intentar con fallback URL
299
+ if (response.status === 404) {
300
+ console.warn(`⚠️ 404 en ${primaryUrl}, intentando con fallback...`);
301
+ const fallbackUrl = buildUrl(this.fallbackBaseUrl, endpoint, params);
302
+ if (this.debug) {
303
+ console.log(`🔄 Retry: ${method} ${fallbackUrl}`);
304
+ }
305
+ // ✅ Segundo intento con fallback
306
+ const fallbackResponse = await fetch(fallbackUrl, fetchOptions);
307
+ // ✅ Manejar 204 No Content
308
+ if (fallbackResponse.status === 204) {
309
+ console.log(`✅ Fallback exitoso (204 No Content): ${endpoint}`);
310
+ return {}; // Retornar objeto vacío
311
+ }
312
+ if (!fallbackResponse.ok) {
313
+ let errorData;
314
+ try {
315
+ errorData = await fallbackResponse.json();
316
+ }
317
+ catch (_a) {
318
+ errorData = { message: fallbackResponse.statusText };
319
+ }
320
+ console.error(`❌ Fallback falló ${fallbackResponse.status}:`, errorData);
321
+ if (fallbackResponse.status === 401) {
322
+ this.eventEmitter.emit("authError", {
323
+ statusCode: 401,
324
+ message: errorData.message || "Authentication required",
325
+ });
326
+ }
327
+ return errorData;
328
+ }
329
+ // ✅ Fallback exitoso con contenido
330
+ const fallbackData = await parseResponse(fallbackResponse);
331
+ console.log(`✅ Fallback exitoso: ${endpoint}`);
332
+ this.eventEmitter.emit("afterRequest", {
333
+ url: fallbackUrl,
334
+ method,
335
+ response: fallbackData,
336
+ });
337
+ return fallbackData;
338
+ }
339
+ // ✅ Manejar 204 No Content en primer intento
340
+ if (response.status === 204) {
341
+ console.log(`✅ Request exitoso (204 No Content): ${endpoint}`);
342
+ return {}; // Retornar objeto vacío
116
343
  }
117
- console.log(`🌐 ${method} ${url}`, fetchOptions);
118
- const response = await fetch(url, fetchOptions);
344
+ // Otros errores (no 404)
119
345
  if (!response.ok) {
120
346
  let errorData;
121
347
  try {
122
348
  errorData = await response.json();
123
349
  }
124
- catch (_a) {
350
+ catch (_b) {
125
351
  errorData = { message: response.statusText };
126
352
  }
127
353
  console.error(`❌ ${method} ${response.status}:`, errorData);
@@ -133,9 +359,10 @@ export class ApiClient {
133
359
  }
134
360
  return errorData;
135
361
  }
362
+ // ✅ Respuesta exitosa del primer intento
136
363
  const responseData = await parseResponse(response);
137
364
  if (useCache && method === "GET") {
138
- const cacheKey = generateCacheKey(method, url, data);
365
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
139
366
  const cacheTime = options.cacheTime || undefined;
140
367
  this.cache.set(cacheKey, responseData, cacheTime);
141
368
  if (this.debug) {
@@ -143,7 +370,7 @@ export class ApiClient {
143
370
  }
144
371
  }
145
372
  this.eventEmitter.emit("afterRequest", {
146
- url,
373
+ url: primaryUrl,
147
374
  method,
148
375
  response: responseData,
149
376
  });
@@ -151,7 +378,7 @@ export class ApiClient {
151
378
  }
152
379
  catch (error) {
153
380
  this.eventEmitter.emit("requestError", {
154
- url,
381
+ url: primaryUrl,
155
382
  method,
156
383
  error,
157
384
  });
@@ -1,5 +1,6 @@
1
1
  export interface SdkConfig {
2
2
  baseUrl: string;
3
+ fallbackBaseUrl?: string;
3
4
  organization: string;
4
5
  token?: string | null;
5
6
  cache?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerosls/clm-sdk",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "SDK for ZeroCLM API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,3 +1,4 @@
1
+ // core/api-client.ts
1
2
  import { SdkConfig, SdkEventType, SdkEvents } from "../types/sdk";
2
3
  import { PaginatedResponse, RequestOptions } from "../types/common";
3
4
  import { EventEmitter } from "./event-emitter";
@@ -12,6 +13,7 @@ import {
12
13
 
13
14
  export class ApiClient {
14
15
  private baseUrl: string;
16
+ private fallbackBaseUrl: string; // ✅ URL de respaldo
15
17
  private organization: string;
16
18
  private token: string | null = null;
17
19
  private eventEmitter: EventEmitter;
@@ -22,6 +24,8 @@ export class ApiClient {
22
24
 
23
25
  constructor(config: SdkConfig, eventEmitter: EventEmitter) {
24
26
  this.baseUrl = config.baseUrl;
27
+ this.fallbackBaseUrl =
28
+ config.fallbackBaseUrl || "http://216.250.117.119/ZeroServicesQA/api/v1";
25
29
  this.organization = config.organization;
26
30
  this.token = config.token || null;
27
31
  this.eventEmitter = eventEmitter;
@@ -122,6 +126,187 @@ export class ApiClient {
122
126
  }
123
127
  }
124
128
 
129
+ /*private async request<T>(
130
+ method: string,
131
+ endpoint: string,
132
+ data?: any,
133
+ params?: Record<string, any>,
134
+ options: RequestOptions = {}
135
+ ): Promise<T> {
136
+ // ✅ Primer intento con baseUrl normal
137
+ const primaryUrl = buildUrl(this.baseUrl, endpoint, params);
138
+
139
+ const base: HeadersInit = buildHeaders(this.token, {
140
+ "X-Organization": this.organization,
141
+ ...(options.headers || {}),
142
+ });
143
+
144
+ const headers = new Headers(base);
145
+
146
+ // ✅ Obtener token legacy
147
+ const legacyToken =
148
+ (window as any).__LEGACY_TOKEN__ ||
149
+ sessionStorage.getItem("legacy_token") ||
150
+ null;
151
+
152
+ if (legacyToken) {
153
+ headers.set("Authorization", `Bearer ${legacyToken}`);
154
+ if (this.debug) {
155
+ console.log("🔐 Using legacy token for:", endpoint);
156
+ }
157
+ } else if (this.token) {
158
+ if (this.debug) {
159
+ console.log("🔐 Using v1 token for:", endpoint);
160
+ }
161
+ } else {
162
+ console.warn("⚠️ No token available for endpoint:", endpoint);
163
+ }
164
+
165
+ const useCache = this.cacheEnabled && options.useCache !== false;
166
+ if (useCache && method === "GET") {
167
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
168
+ const cachedData = this.cache.get<T>(cacheKey);
169
+ if (cachedData) {
170
+ if (this.debug) {
171
+ console.log(`[SDK-Cache] Hit: ${cacheKey}`);
172
+ }
173
+ return cachedData;
174
+ }
175
+ }
176
+
177
+ this.eventEmitter.emit("beforeRequest", {
178
+ url: primaryUrl,
179
+ method,
180
+ data,
181
+ });
182
+
183
+ try {
184
+ const fetchOptions: RequestInit = {
185
+ method,
186
+ headers,
187
+ credentials: "include",
188
+ };
189
+
190
+ if (data && method !== "GET") {
191
+ fetchOptions.body = JSON.stringify(data);
192
+ if (this.debug) {
193
+ console.log(`📤 ${method} Body:`, data);
194
+ }
195
+ }
196
+
197
+ if (this.debug) {
198
+ console.log(`🌐 ${method} ${primaryUrl}`);
199
+ }
200
+
201
+ // ✅ Primer intento
202
+ const response = await fetch(primaryUrl, fetchOptions);
203
+
204
+ // ✅ Si es 404, intentar con fallback URL
205
+ if (response.status === 404) {
206
+ console.warn(`⚠️ 404 en ${primaryUrl}, intentando con fallback...`);
207
+
208
+ const fallbackUrl = buildUrl(this.fallbackBaseUrl, endpoint, params);
209
+
210
+ if (this.debug) {
211
+ console.log(`🔄 Retry: ${method} ${fallbackUrl}`);
212
+ }
213
+
214
+ // ✅ Segundo intento con fallback
215
+ const fallbackResponse = await fetch(fallbackUrl, fetchOptions);
216
+
217
+ if (!fallbackResponse.ok) {
218
+ let errorData;
219
+ try {
220
+ errorData = await fallbackResponse.json();
221
+ } catch {
222
+ errorData = { message: fallbackResponse.statusText };
223
+ }
224
+
225
+ console.error(`❌ Fallback también falló ${fallbackResponse.status}:`, errorData);
226
+
227
+ if (fallbackResponse.status === 401) {
228
+ this.eventEmitter.emit("authError", {
229
+ statusCode: 401,
230
+ message: errorData.message || "Authentication required",
231
+ });
232
+ }
233
+
234
+ return errorData as T;
235
+ }
236
+
237
+ // ✅ Fallback exitoso
238
+ const fallbackData = await parseResponse<T>(fallbackResponse);
239
+
240
+ console.log(`✅ Fallback exitoso para: ${endpoint}`);
241
+
242
+ this.eventEmitter.emit("afterRequest", {
243
+ url: fallbackUrl,
244
+ method,
245
+ response: fallbackData,
246
+ });
247
+
248
+ return fallbackData;
249
+ }
250
+
251
+ // ✅ Otros errores (no 404)
252
+ if (!response.ok) {
253
+ let errorData;
254
+ try {
255
+ errorData = await response.json();
256
+ } catch {
257
+ errorData = { message: response.statusText };
258
+ }
259
+
260
+ console.error(`❌ ${method} ${response.status}:`, errorData);
261
+
262
+ if (response.status === 401) {
263
+ this.eventEmitter.emit("authError", {
264
+ statusCode: 401,
265
+ message: errorData.message || "Authentication required",
266
+ });
267
+ }
268
+
269
+ return errorData as T;
270
+ }
271
+
272
+ // ✅ Respuesta exitosa del primer intento
273
+ const responseData = await parseResponse<T>(response);
274
+
275
+ if (useCache && method === "GET") {
276
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
277
+ const cacheTime = options.cacheTime || undefined;
278
+ this.cache.set(cacheKey, responseData, cacheTime);
279
+
280
+ if (this.debug) {
281
+ console.log(`[SDK-Cache] Set: ${cacheKey}`);
282
+ }
283
+ }
284
+
285
+ this.eventEmitter.emit("afterRequest", {
286
+ url: primaryUrl,
287
+ method,
288
+ response: responseData,
289
+ });
290
+
291
+ return responseData;
292
+ } catch (error) {
293
+ this.eventEmitter.emit("requestError", {
294
+ url: primaryUrl,
295
+ method,
296
+ error,
297
+ });
298
+
299
+ if (error instanceof ApiError) {
300
+ throw error;
301
+ }
302
+
303
+ throw new ApiError((error as Error).message || "Network error", 0, {
304
+ originalError: error,
305
+ });
306
+ }
307
+ }*/
308
+
309
+ // core/api-client.ts
125
310
  private async request<T>(
126
311
  method: string,
127
312
  endpoint: string,
@@ -129,7 +314,7 @@ export class ApiClient {
129
314
  params?: Record<string, any>,
130
315
  options: RequestOptions = {}
131
316
  ): Promise<T> {
132
- const url = buildUrl(this.baseUrl, endpoint, params);
317
+ const primaryUrl = buildUrl(this.baseUrl, endpoint, params);
133
318
 
134
319
  const base: HeadersInit = buildHeaders(this.token, {
135
320
  "X-Organization": this.organization,
@@ -138,10 +323,7 @@ export class ApiClient {
138
323
 
139
324
  const headers = new Headers(base);
140
325
 
141
- //const legacyPattern = /(^|\/)legacy(\/|$)/i;
142
- //const isLegacyEndpoint = legacyPattern.test(endpoint);
143
-
144
- //if (isLegacyEndpoint) {
326
+ // Obtener token legacy
145
327
  const legacyToken =
146
328
  (window as any).__LEGACY_TOKEN__ ||
147
329
  sessionStorage.getItem("legacy_token") ||
@@ -149,12 +331,10 @@ export class ApiClient {
149
331
 
150
332
  if (legacyToken) {
151
333
  headers.set("Authorization", `Bearer ${legacyToken}`);
152
-
153
334
  if (this.debug) {
154
335
  console.log("🔐 Using legacy token for:", endpoint);
155
336
  }
156
337
  } else if (this.token) {
157
- // ✅ Si no hay legacy token, usar token v1
158
338
  if (this.debug) {
159
339
  console.log("🔐 Using v1 token for:", endpoint);
160
340
  }
@@ -162,15 +342,9 @@ export class ApiClient {
162
342
  console.warn("⚠️ No token available for endpoint:", endpoint);
163
343
  }
164
344
 
165
- //} else {
166
- // if (!this.token) {
167
- // headers.delete("Authorization");
168
- // }
169
- //}
170
-
171
345
  const useCache = this.cacheEnabled && options.useCache !== false;
172
346
  if (useCache && method === "GET") {
173
- const cacheKey = generateCacheKey(method, url, data);
347
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
174
348
  const cachedData = this.cache.get<T>(cacheKey);
175
349
  if (cachedData) {
176
350
  if (this.debug) {
@@ -181,7 +355,7 @@ export class ApiClient {
181
355
  }
182
356
 
183
357
  this.eventEmitter.emit("beforeRequest", {
184
- url,
358
+ url: primaryUrl,
185
359
  method,
186
360
  data,
187
361
  });
@@ -195,12 +369,81 @@ export class ApiClient {
195
369
 
196
370
  if (data && method !== "GET") {
197
371
  fetchOptions.body = JSON.stringify(data);
198
- console.log(`📤 ${method} Body:`, data);
372
+ if (this.debug) {
373
+ console.log(`📤 ${method} Body:`, data);
374
+ }
199
375
  }
200
376
 
201
- console.log(`🌐 ${method} ${url}`, fetchOptions);
202
- const response = await fetch(url, fetchOptions);
377
+ if (this.debug) {
378
+ console.log(`🌐 ${method} ${primaryUrl}`);
379
+ }
380
+
381
+ // ✅ Primer intento
382
+ const response = await fetch(primaryUrl, fetchOptions);
383
+
384
+ // ✅ Si es 404, intentar con fallback URL
385
+ if (response.status === 404) {
386
+ console.warn(`⚠️ 404 en ${primaryUrl}, intentando con fallback...`);
387
+
388
+ const fallbackUrl = buildUrl(this.fallbackBaseUrl, endpoint, params);
389
+
390
+ if (this.debug) {
391
+ console.log(`🔄 Retry: ${method} ${fallbackUrl}`);
392
+ }
393
+
394
+ // ✅ Segundo intento con fallback
395
+ const fallbackResponse = await fetch(fallbackUrl, fetchOptions);
396
+
397
+ // ✅ Manejar 204 No Content
398
+ if (fallbackResponse.status === 204) {
399
+ console.log(`✅ Fallback exitoso (204 No Content): ${endpoint}`);
400
+ return {} as T; // Retornar objeto vacío
401
+ }
402
+
403
+ if (!fallbackResponse.ok) {
404
+ let errorData;
405
+ try {
406
+ errorData = await fallbackResponse.json();
407
+ } catch {
408
+ errorData = { message: fallbackResponse.statusText };
409
+ }
410
+
411
+ console.error(
412
+ `❌ Fallback falló ${fallbackResponse.status}:`,
413
+ errorData
414
+ );
415
+
416
+ if (fallbackResponse.status === 401) {
417
+ this.eventEmitter.emit("authError", {
418
+ statusCode: 401,
419
+ message: errorData.message || "Authentication required",
420
+ });
421
+ }
422
+
423
+ return errorData as T;
424
+ }
425
+
426
+ // ✅ Fallback exitoso con contenido
427
+ const fallbackData = await parseResponse<T>(fallbackResponse);
428
+
429
+ console.log(`✅ Fallback exitoso: ${endpoint}`);
430
+
431
+ this.eventEmitter.emit("afterRequest", {
432
+ url: fallbackUrl,
433
+ method,
434
+ response: fallbackData,
435
+ });
436
+
437
+ return fallbackData;
438
+ }
439
+
440
+ // ✅ Manejar 204 No Content en primer intento
441
+ if (response.status === 204) {
442
+ console.log(`✅ Request exitoso (204 No Content): ${endpoint}`);
443
+ return {} as T; // Retornar objeto vacío
444
+ }
203
445
 
446
+ // ✅ Otros errores (no 404)
204
447
  if (!response.ok) {
205
448
  let errorData;
206
449
  try {
@@ -221,10 +464,11 @@ export class ApiClient {
221
464
  return errorData as T;
222
465
  }
223
466
 
467
+ // ✅ Respuesta exitosa del primer intento
224
468
  const responseData = await parseResponse<T>(response);
225
469
 
226
470
  if (useCache && method === "GET") {
227
- const cacheKey = generateCacheKey(method, url, data);
471
+ const cacheKey = generateCacheKey(method, primaryUrl, data);
228
472
  const cacheTime = options.cacheTime || undefined;
229
473
  this.cache.set(cacheKey, responseData, cacheTime);
230
474
 
@@ -234,7 +478,7 @@ export class ApiClient {
234
478
  }
235
479
 
236
480
  this.eventEmitter.emit("afterRequest", {
237
- url,
481
+ url: primaryUrl,
238
482
  method,
239
483
  response: responseData,
240
484
  });
@@ -242,7 +486,7 @@ export class ApiClient {
242
486
  return responseData;
243
487
  } catch (error) {
244
488
  this.eventEmitter.emit("requestError", {
245
- url,
489
+ url: primaryUrl,
246
490
  method,
247
491
  error,
248
492
  });
package/src/types/sdk.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export interface SdkConfig {
2
2
  baseUrl: string;
3
+ fallbackBaseUrl?: string;
3
4
  organization: string;
4
5
  token?: string | null;
5
6
  cache?: {