kmod-cli 1.8.0 → 1.8.2

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.
@@ -18,23 +18,23 @@ import Cookies from 'js-cookie';
18
18
  export interface Pagination {
19
19
  current?: number;
20
20
  pageSize?: number;
21
- mode?: 'server' | 'client';
21
+ mode?: "server" | "client";
22
22
  }
23
23
 
24
24
  export interface Sorter {
25
25
  field: string;
26
- order: 'asc' | 'desc';
26
+ order: "asc" | "desc";
27
27
  }
28
28
 
29
- export type FilterOperator =
30
- | 'eq'
31
- | 'ne'
32
- | 'lt'
33
- | 'lte'
34
- | 'gt'
35
- | 'gte'
36
- | 'in'
37
- | 'contains';
29
+ export type FilterOperator =
30
+ | "eq"
31
+ | "ne"
32
+ | "lt"
33
+ | "lte"
34
+ | "gt"
35
+ | "gte"
36
+ | "in"
37
+ | "contains";
38
38
 
39
39
  export interface Filter {
40
40
  field: string;
@@ -93,7 +93,7 @@ export interface DeleteManyParams {
93
93
 
94
94
  export interface CustomParams extends AxiosRequestConfig {
95
95
  url?: string;
96
- method?: 'get' | 'post' | 'put' | 'patch' | 'delete';
96
+ method?: "get" | "post" | "put" | "patch" | "delete";
97
97
  payload?: any;
98
98
  query?: Record<string, any>;
99
99
  headers?: Record<string, string>;
@@ -162,6 +162,11 @@ export interface UseMutationOptions<TData = any> {
162
162
 
163
163
  export type Payload = any;
164
164
 
165
+ type ApiError = {
166
+ message: string;
167
+ statusCode?: number;
168
+ };
169
+
165
170
  // ============ UTILITY FUNCTIONS ============
166
171
 
167
172
  /**
@@ -172,14 +177,14 @@ function normalizeResponseData<T>(response: any): T[] {
172
177
  if (Array.isArray(response)) {
173
178
  return response;
174
179
  }
175
-
180
+
176
181
  // Case 2: Object with 'data' property
177
- if (response && typeof response === 'object') {
178
- return response
182
+ if (response && typeof response === "object") {
183
+ return response;
179
184
  }
180
-
185
+
181
186
  // Fallback: empty array
182
- console.warn('Unable to extract array data from response:', response);
187
+ console.warn("Unable to extract array data from response:", response);
183
188
  return [];
184
189
  }
185
190
 
@@ -188,19 +193,19 @@ function normalizeResponseData<T>(response: any): T[] {
188
193
  */
189
194
  function extractTotalCount(response: any, dataLength: number): number {
190
195
  // Check headers first
191
- if (response.headers?.['x-total-count']) {
192
- const count = parseInt(response.headers['x-total-count'], 10);
196
+ if (response.headers?.["x-total-count"]) {
197
+ const count = parseInt(response.headers["x-total-count"], 10);
193
198
  if (!isNaN(count)) return count;
194
199
  }
195
-
200
+
196
201
  // Check response body
197
202
  const data = response.data;
198
- if (data && typeof data === 'object') {
199
- if (typeof data.total === 'number') return data.total;
200
- if (typeof data.totalCount === 'number') return data.totalCount;
201
- if (typeof data.count === 'number') return data.count;
203
+ if (data && typeof data === "object") {
204
+ if (typeof data.total === "number") return data.total;
205
+ if (typeof data.totalCount === "number") return data.totalCount;
206
+ if (typeof data.count === "number") return data.count;
202
207
  }
203
-
208
+
204
209
  // Fallback to data length
205
210
  return dataLength;
206
211
  }
@@ -217,31 +222,33 @@ class DataProvider {
217
222
  private options: Required<DataProviderOptions>;
218
223
 
219
224
  constructor(
220
- httpClient: AxiosInstance = axios.create(),
221
- options: DataProviderOptions = {}
225
+ httpClient: AxiosInstance = axios.create(),
226
+ options: DataProviderOptions = {},
222
227
  ) {
223
228
  this.httpClient = httpClient;
224
229
 
225
- const baseURL = httpClient.defaults.baseURL || '';
226
- this.apiUrl = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
227
-
230
+ const baseURL = httpClient.defaults.baseURL || "";
231
+ this.apiUrl = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
232
+
228
233
  if (!this.apiUrl) {
229
- console.warn('[DataProvider] No baseURL found in httpClient. Please set baseURL when creating httpClient.');
234
+ console.warn(
235
+ "[DataProvider] No baseURL found in httpClient. Please set baseURL when creating httpClient.",
236
+ );
230
237
  }
231
-
238
+
232
239
  this.cache = new Map();
233
240
  this.options = {
234
241
  cacheTime: 5 * 60 * 1000,
235
242
  retryCount: 1,
236
243
  retryDelay: 1000,
237
244
  debug: false,
238
- ...options
245
+ ...options,
239
246
  };
240
247
  }
241
248
 
242
249
  private log(message: string, data?: any): void {
243
250
  if (this.options.debug) {
244
- console.log(`[DataProvider] ${message}`, data || '');
251
+ // console.log(`[DataProvider] ${message}`, data || '');
245
252
  }
246
253
  }
247
254
 
@@ -253,43 +260,46 @@ class DataProvider {
253
260
  private getCache<T = any>(key: string): T | null {
254
261
  const cached = this.cache.get(key);
255
262
  if (!cached) return null;
256
-
263
+
257
264
  const now = Date.now();
258
265
  if (now - cached.timestamp > this.options.cacheTime) {
259
266
  this.cache.delete(key);
260
- this.log('Cache expired', key);
267
+ // this.log('Cache expired', key);
261
268
  return null;
262
269
  }
263
-
264
- this.log('Cache hit', key);
270
+
271
+ // this.log('Cache hit', key);
265
272
  return cached.data as T;
266
273
  }
267
274
 
268
275
  private setCache<T = any>(key: string, data: T): void {
269
276
  this.cache.set(key, {
270
277
  data,
271
- timestamp: Date.now()
278
+ timestamp: Date.now(),
272
279
  });
273
- this.log('Cache set', key);
280
+ // this.log('Cache set', key);
274
281
  }
275
282
 
276
283
  public invalidateCache(resource: string, id?: string | number): void {
277
284
  const keys = Array.from(this.cache.keys());
278
-
285
+
279
286
  if (id !== undefined) {
280
287
  // Invalidate specific item and related lists
281
- keys.forEach(key => {
282
- if (key.startsWith(`${resource}:`) || key.startsWith(`${resource}/${id}:`)) {
288
+ keys.forEach((key) => {
289
+ if (
290
+ key.startsWith(`${resource}:`) ||
291
+ key.startsWith(`${resource}/${id}:`)
292
+ ) {
283
293
  this.cache.delete(key);
284
- this.log('Cache invalidated', key);
294
+ // this.log('Cache invalidated', key);
285
295
  }
286
296
  });
287
297
  } else {
288
298
  // Invalidate entire resource
289
- keys.forEach(key => {
299
+ keys.forEach((key) => {
290
300
  if (key.startsWith(`${resource}:`)) {
291
301
  this.cache.delete(key);
292
- this.log('Cache invalidated', key);
302
+ // this.log('Cache invalidated', key);
293
303
  }
294
304
  });
295
305
  }
@@ -297,371 +307,382 @@ class DataProvider {
297
307
 
298
308
  public clearAllCache(): void {
299
309
  this.cache.clear();
300
- this.log('All cache cleared');
310
+ // this.log('All cache cleared');
301
311
  }
302
312
 
303
313
  // Retry logic
304
314
  private async retryRequest<T>(
305
- fn: () => Promise<T>,
306
- retries: number = this.options.retryCount
315
+ fn: () => Promise<T>,
316
+ retries: number = this.options.retryCount,
307
317
  ): Promise<T> {
308
318
  try {
309
319
  return await fn();
310
320
  } catch (error) {
311
321
  if (retries <= 0) throw error;
312
-
322
+
313
323
  // Don't retry on 4xx errors (client errors)
314
324
  const axiosError = error as AxiosError;
315
325
  if (
316
- axiosError.response &&
317
- axiosError.response.status >= 400 &&
326
+ axiosError.response &&
327
+ axiosError.response.status >= 400 &&
318
328
  axiosError.response.status < 500
319
329
  ) {
320
330
  throw error;
321
331
  }
322
-
323
- this.log(`Retrying... (${this.options.retryCount - retries + 1}/${this.options.retryCount})`);
324
-
325
- await new Promise(resolve =>
326
- setTimeout(resolve, this.options.retryDelay)
332
+
333
+ // this.log(`Retrying... (${this.options.retryCount - retries + 1}/${this.options.retryCount})`);
334
+
335
+ await new Promise((resolve) =>
336
+ setTimeout(resolve, this.options.retryDelay),
327
337
  );
328
-
338
+
329
339
  return this.retryRequest(fn, retries - 1);
330
340
  }
331
341
  }
332
342
 
333
343
  // CRUD Methods
334
344
  async getList<T = any>(
335
- resource: string,
336
- params: GetListParams = {},
337
- useCache: boolean = true
345
+ resource: string,
346
+ params: GetListParams = {},
347
+ useCache: boolean = true,
338
348
  ): Promise<GetListResponse<T>> {
339
349
  const cacheKey = this.getCacheKey(resource, params);
340
-
350
+
341
351
  if (useCache) {
342
352
  const cached = this.getCache<GetListResponse<T>>(cacheKey);
343
353
  if (cached) return cached;
344
354
  }
345
-
355
+
346
356
  const { pagination, filters, sorters, meta } = params;
347
357
  const url = `${this.apiUrl}/${resource}`;
348
358
  const query: Record<string, any> = {};
349
-
359
+
350
360
  if (pagination) {
351
361
  const { current = 1, pageSize = 10 } = pagination;
352
362
  query._start = (current - 1) * pageSize;
353
363
  query._limit = pageSize;
354
364
  }
355
-
365
+
356
366
  if (sorters && sorters.length > 0) {
357
- query._sort = sorters.map(s => s.field).join(',');
358
- query._order = sorters.map(s => s.order).join(',');
367
+ query._sort = sorters.map((s) => s.field).join(",");
368
+ query._order = sorters.map((s) => s.order).join(",");
359
369
  }
360
-
370
+
361
371
  if (filters && filters.length > 0) {
362
- filters.forEach(filter => {
372
+ filters.forEach((filter) => {
363
373
  const { field, operator, value } = filter;
364
374
  switch (operator) {
365
- case 'eq':
366
- query[field] = value;
375
+ case "eq":
376
+ query[field] = value;
367
377
  break;
368
- case 'ne':
369
- query[`${field}_ne`] = value;
378
+ case "ne":
379
+ query[`${field}_ne`] = value;
370
380
  break;
371
- case 'lt':
372
- query[`${field}_lt`] = value;
381
+ case "lt":
382
+ query[`${field}_lt`] = value;
373
383
  break;
374
- case 'lte':
375
- query[`${field}_lte`] = value;
384
+ case "lte":
385
+ query[`${field}_lte`] = value;
376
386
  break;
377
- case 'gt':
378
- query[`${field}_gt`] = value;
387
+ case "gt":
388
+ query[`${field}_gt`] = value;
379
389
  break;
380
- case 'gte':
381
- query[`${field}_gte`] = value;
390
+ case "gte":
391
+ query[`${field}_gte`] = value;
382
392
  break;
383
- case 'in':
384
- query[`${field}_in`] = Array.isArray(value) ? value.join(',') : value;
393
+ case "in":
394
+ query[`${field}_in`] = Array.isArray(value)
395
+ ? value.join(",")
396
+ : value;
385
397
  break;
386
- case 'contains':
387
- query[`${field}_like`] = value;
398
+ case "contains":
399
+ query[`${field}_like`] = value;
388
400
  break;
389
401
  }
390
402
  });
391
403
  }
392
-
404
+
393
405
  try {
394
406
  const result = await this.retryRequest(async () => {
395
- this.log(`GET ${url}`, query);
396
-
397
- const response = await this.httpClient.get(url, {
407
+ // this.log(`GET ${url}`, query);
408
+
409
+ const response = await this.httpClient.get(url, {
398
410
  params: query,
399
- ...meta
411
+ ...meta,
400
412
  });
401
-
413
+
402
414
  const data = normalizeResponseData<T>(response.data);
403
415
  const total = extractTotalCount(response, data.length);
404
-
405
- this.log(`Response: ${data.length} items, total: ${total}`);
406
-
416
+
417
+ // this.log(`Response: ${data.length} items, total: ${total}`);
418
+
407
419
  return { data, total };
408
420
  });
409
-
421
+
410
422
  if (useCache) {
411
423
  this.setCache(cacheKey, result);
412
424
  }
413
-
425
+
414
426
  return result;
415
427
  } catch (error) {
416
- this.log('Error in getList', error);
417
- throw this.handleError(error);
428
+ const apiError = this.handleError(error);
429
+ return Promise.reject(apiError);
418
430
  }
419
431
  }
420
432
 
421
433
  async getOne<T = any>(
422
- resource: string,
423
- params: GetOneParams,
424
- useCache: boolean = true
434
+ resource: string,
435
+ params: GetOneParams,
436
+ useCache: boolean = true,
425
437
  ): Promise<GetOneResponse<T>> {
426
438
  const { id, meta } = params;
427
439
  const cacheKey = this.getCacheKey(`${resource}/${id}`, {});
428
-
440
+
429
441
  if (useCache) {
430
442
  const cached = this.getCache<GetOneResponse<T>>(cacheKey);
431
443
  if (cached) return cached;
432
444
  }
433
-
445
+
434
446
  const url = `${this.apiUrl}/${resource}/${id}`;
435
-
447
+
436
448
  try {
437
449
  const result = await this.retryRequest(async () => {
438
- this.log(`GET ${url}`);
450
+ // this.log(`GET ${url}`);
439
451
  const response = await this.httpClient.get<T>(url, meta);
440
-
452
+
441
453
  // Handle wrapped response
442
- const data = (response.data as any) || response
443
-
454
+ const data = (response.data as any) || response;
455
+
444
456
  return { data };
445
457
  });
446
-
458
+
447
459
  if (useCache) {
448
460
  this.setCache(cacheKey, result);
449
461
  }
450
-
462
+
451
463
  return result;
452
464
  } catch (error) {
453
- this.log('Error in getOne', error);
454
- throw this.handleError(error);
465
+ const apiError = this.handleError(error);
466
+ return Promise.reject(apiError);
455
467
  }
456
468
  }
457
469
 
458
470
  async getMany<T = any>(
459
- resource: string,
460
- params: GetManyParams,
461
- useCache: boolean = true
471
+ resource: string,
472
+ params: GetManyParams,
473
+ useCache: boolean = true,
462
474
  ): Promise<GetManyResponse<T>> {
463
475
  const { ids, meta } = params;
464
476
  const url = `${this.apiUrl}/${resource}`;
465
-
477
+
466
478
  try {
467
479
  const result = await this.retryRequest(async () => {
468
- this.log(`GET ${url} with ids`, ids);
469
-
480
+ // this.log(`GET ${url} with ids`, ids);
481
+
470
482
  const response = await this.httpClient.get(url, {
471
483
  params: { id: ids },
472
- ...meta
484
+ ...meta,
473
485
  });
474
-
486
+
475
487
  const data = normalizeResponseData<T>(response.data);
476
-
488
+
477
489
  return { data };
478
490
  });
479
-
491
+
480
492
  return result;
481
493
  } catch (error) {
482
- this.log('Error in getMany', error);
483
- throw this.handleError(error);
494
+ const apiError = this.handleError(error);
495
+ return Promise.reject(apiError);
484
496
  }
485
497
  }
486
498
 
487
499
  async create<T = any, V = any>(
488
- resource: string,
489
- params: CreateParams<V>
500
+ resource: string,
501
+ params: CreateParams<V>,
490
502
  ): Promise<CreateResponse<T>> {
491
503
  const { variables, meta } = params;
492
504
  const url = `${this.apiUrl}/${resource}`;
493
-
505
+
494
506
  try {
495
507
  const result = await this.retryRequest(async () => {
496
- this.log(`POST ${url}`, variables);
508
+ // this.log(`POST ${url}`, variables);
497
509
  const response = await this.httpClient.post<T>(url, variables, meta);
498
-
510
+
499
511
  // Handle wrapped response
500
- const data = (response.data as any) || response
501
-
512
+ const data = (response.data as any) || response;
513
+
502
514
  return { data };
503
515
  });
504
-
516
+
505
517
  this.invalidateCache(resource);
506
518
  return result;
507
519
  } catch (error) {
508
- this.log('Error in create', error);
509
- throw this.handleError(error);
520
+ const apiError = this.handleError(error);
521
+ return Promise.reject(apiError);
510
522
  }
511
523
  }
512
524
 
513
525
  async createMany<T = any, V = any>(
514
- resource: string,
515
- params: CreateManyParams<V>
526
+ resource: string,
527
+ params: CreateManyParams<V>,
516
528
  ): Promise<GetManyResponse<T>> {
517
529
  const { variables, meta } = params;
518
-
530
+
519
531
  try {
520
532
  const result = await this.retryRequest(async () => {
521
- this.log(`POST MANY ${this.apiUrl}/${resource}`, variables);
522
-
533
+ // this.log(`POST MANY ${this.apiUrl}/${resource}`, variables);
534
+
523
535
  const responses = await Promise.all(
524
- variables.map(variable =>
525
- this.httpClient.post<T>(`${this.apiUrl}/${resource}`, variable, meta)
526
- )
536
+ variables.map((variable) =>
537
+ this.httpClient.post<T>(
538
+ `${this.apiUrl}/${resource}`,
539
+ variable,
540
+ meta,
541
+ ),
542
+ ),
527
543
  );
528
-
529
- const data = responses.map(r => (r.data as any)?.data || r.data);
530
-
544
+
545
+ const data = responses.map((r) => (r.data as any)?.data || r.data);
546
+
531
547
  return { data };
532
548
  });
533
-
549
+
534
550
  this.invalidateCache(resource);
535
551
  return result;
536
552
  } catch (error) {
537
- this.log('Error in createMany', error);
538
- throw this.handleError(error);
553
+ const apiError = this.handleError(error);
554
+ return Promise.reject(apiError);
539
555
  }
540
556
  }
541
557
 
542
558
  async update<T = any, V = any>(
543
- resource: string,
544
- params: UpdateParams<V>
559
+ resource: string,
560
+ params: UpdateParams<V>,
545
561
  ): Promise<UpdateResponse<T>> {
546
562
  const { id, variables, meta } = params;
547
563
  const url = `${this.apiUrl}/${resource}/${id}`;
548
-
564
+
549
565
  try {
550
566
  const result = await this.retryRequest(async () => {
551
- this.log(`PATCH ${url}`, variables);
567
+ // this.log(`PATCH ${url}`, variables);
552
568
  const response = await this.httpClient.patch<T>(url, variables, meta);
553
-
569
+
554
570
  // Handle wrapped response
555
- const data = (response.data as any) || response
556
-
571
+ const data = (response.data as any) || response;
572
+
557
573
  return { data };
558
574
  });
559
-
575
+
560
576
  this.invalidateCache(resource, id);
561
577
  return result;
562
578
  } catch (error) {
563
- this.log('Error in update', error);
564
- throw this.handleError(error);
579
+ const apiError = this.handleError(error);
580
+ return Promise.reject(apiError);
565
581
  }
566
582
  }
567
583
 
568
584
  async updateMany<T = any, V = any>(
569
- resource: string,
570
- params: UpdateManyParams<V>
585
+ resource: string,
586
+ params: UpdateManyParams<V>,
571
587
  ): Promise<GetManyResponse<T>> {
572
588
  const { ids, variables, meta } = params;
573
-
589
+
574
590
  try {
575
591
  const result = await this.retryRequest(async () => {
576
- this.log(`PATCH MANY ${this.apiUrl}/${resource}`, { ids, variables });
577
-
592
+ // this.log(`PATCH MANY ${this.apiUrl}/${resource}`, { ids, variables });
593
+
578
594
  const responses = await Promise.all(
579
- ids.map(id =>
595
+ ids.map((id) =>
580
596
  this.httpClient.patch<T>(
581
597
  `${this.apiUrl}/${resource}/${id}`,
582
598
  variables,
583
- meta
584
- )
585
- )
599
+ meta,
600
+ ),
601
+ ),
586
602
  );
587
-
588
- const data = responses.map(r => (r.data as any)?.data || r.data);
589
-
603
+
604
+ const data = responses.map((r) => (r.data as any)?.data || r.data);
605
+
590
606
  return { data };
591
607
  });
592
-
593
- ids.forEach(id => this.invalidateCache(resource, id));
608
+
609
+ ids.forEach((id) => this.invalidateCache(resource, id));
594
610
  return result;
595
611
  } catch (error) {
596
- this.log('Error in updateMany', error);
597
- throw this.handleError(error);
612
+ const apiError = this.handleError(error);
613
+ return Promise.reject(apiError);
598
614
  }
599
615
  }
600
616
 
601
617
  async deleteOne<T = any>(
602
- resource: string,
603
- params: DeleteOneParams
618
+ resource: string,
619
+ params: DeleteOneParams,
604
620
  ): Promise<DeleteResponse<T>> {
605
621
  const { id, meta } = params;
606
622
  const url = `${this.apiUrl}/${resource}/${id}`;
607
-
623
+
608
624
  try {
609
625
  const result = await this.retryRequest(async () => {
610
- this.log(`DELETE ${url}`);
626
+ // this.log(`DELETE ${url}`);
611
627
  const response = await this.httpClient.delete<T>(url, meta);
612
-
628
+
613
629
  // Handle wrapped response
614
- const data = (response.data as any) || response
615
-
630
+ const data = (response.data as any) || response;
631
+
616
632
  return { data };
617
633
  });
618
-
634
+
619
635
  this.invalidateCache(resource, id);
620
636
  return result;
621
637
  } catch (error) {
622
- this.log('Error in deleteOne', error);
623
- throw this.handleError(error);
638
+ const apiError = this.handleError(error);
639
+ return Promise.reject(apiError);
624
640
  }
625
641
  }
626
642
 
627
643
  async deleteMany<T = any>(
628
- resource: string,
629
- params: DeleteManyParams
644
+ resource: string,
645
+ params: DeleteManyParams,
630
646
  ): Promise<GetManyResponse<T>> {
631
647
  const { ids, meta } = params;
632
-
648
+
633
649
  try {
634
650
  const result = await this.retryRequest(async () => {
635
- this.log(`DELETE MANY ${this.apiUrl}/${resource}`, ids);
636
-
651
+ // this.log(`DELETE MANY ${this.apiUrl}/${resource}`, ids);
652
+
637
653
  const responses = await Promise.all(
638
- ids.map(id =>
639
- this.httpClient.delete<T>(`${this.apiUrl}/${resource}/${id}`, meta)
640
- )
654
+ ids.map((id) =>
655
+ this.httpClient.delete<T>(`${this.apiUrl}/${resource}/${id}`, meta),
656
+ ),
641
657
  );
642
-
643
- const data = responses.map(r => (r.data as any)?.data || r.data);
644
-
658
+
659
+ const data = responses.map((r) => (r.data as any)?.data || r.data);
660
+
645
661
  return { data };
646
662
  });
647
-
648
- ids.forEach(id => this.invalidateCache(resource, id));
663
+
664
+ ids.forEach((id) => this.invalidateCache(resource, id));
649
665
  return result;
650
666
  } catch (error) {
651
- this.log('Error in deleteMany', error);
652
- throw this.handleError(error);
667
+ const apiError = this.handleError(error);
668
+ return Promise.reject(apiError);
653
669
  }
654
670
  }
655
671
 
656
672
  async custom<T = any>(params: CustomParams): Promise<CustomResponse<T>> {
657
- const { url , method = 'get', payload, query, headers } = params;
673
+ const { url, method = "get", payload, query, headers } = params;
658
674
 
659
- if (!url) throw this.handleError("No url provided");
675
+ if (!url) {
676
+ return Promise.reject({
677
+ message: "No url provided",
678
+ statusCode: 0,
679
+ });
680
+ }
660
681
  try {
661
682
  return await this.retryRequest(async () => {
662
683
  // if url is not absolute, assume it's a relative path
663
- const requestUrl = url.startsWith('http') ? url : url;
664
- this.log(`${method.toUpperCase()} ${requestUrl}`, { payload, query });
684
+ const requestUrl = url.startsWith("http") ? url : url;
685
+ // this.log(`${method.toUpperCase()} ${requestUrl}`, { payload, query });
665
686
 
666
687
  const response = await this.httpClient<T>({
667
688
  url: requestUrl,
@@ -671,40 +692,38 @@ class DataProvider {
671
692
  headers,
672
693
  });
673
694
 
674
-
675
695
  return { data: (response?.data as any) || response };
676
696
  });
677
697
  } catch (error) {
678
- this.log('Error in custom', error);
679
- throw this.handleError(error);
698
+ const apiError = this.handleError(error);
699
+ return Promise.reject(apiError);
680
700
  }
681
701
  }
682
702
 
683
703
  private handleError(error: unknown): DataProviderError {
684
704
  const axiosError = error as AxiosError<any>;
685
-
705
+
686
706
  if (axiosError.response) {
687
- const responseData = axiosError.response.data;
688
-
707
+ const data = axiosError.response.data;
708
+
709
+ this.log("API ERROR", {
710
+ status: axiosError.response.status,
711
+ data,
712
+ });
713
+
689
714
  return {
690
- message: responseData?.message ||
691
- responseData?.error ||
692
- axiosError.message ||
693
- 'Request failed',
715
+ message: data?.message || data?.error || "Request failed",
694
716
  statusCode: axiosError.response.status,
695
- errors: responseData?.errors || responseData?.details,
696
- };
697
- } else if (axiosError.request) {
698
- return {
699
- message: 'Network error - no response received',
700
- statusCode: 0,
701
717
  };
702
718
  }
703
-
704
- return {
705
- message: (error as Error).message || 'Unknown error',
706
- statusCode: 0,
707
- };
719
+
720
+ if (axiosError.request) {
721
+ this.log("NETWORK ERROR", axiosError.request);
722
+ return { message: "Network error", statusCode: 0 };
723
+ }
724
+
725
+ this.log("UNKNOWN ERROR", error);
726
+ return { message: "Unknown error", statusCode: 0 };
708
727
  }
709
728
  }
710
729
 
@@ -715,7 +734,7 @@ export const DataProviderContext = createContext<DataProvider | null>(null);
715
734
  export function useList<T = any>(
716
735
  resource: string,
717
736
  params: GetListParams = {},
718
- options: UseListOptions = {}
737
+ options: UseListOptions = {},
719
738
  ) {
720
739
  const [data, setData] = useState<T[] | T | any | null>(null);
721
740
  const [total, setTotal] = useState<number>(0);
@@ -723,20 +742,23 @@ export function useList<T = any>(
723
742
  const [error, setError] = useState<DataProviderError | null>(null);
724
743
 
725
744
  const dataProvider = useDataProvider();
726
-
745
+
727
746
  const { refetchInterval, enabled = true } = options;
728
747
  const paramsStr = JSON.stringify(params);
729
-
748
+
730
749
  const refetch = useCallback(async () => {
731
750
  if (!enabled) {
732
751
  setLoading(false);
733
752
  return;
734
753
  }
735
-
754
+
736
755
  try {
737
756
  setLoading(true);
738
757
  setError(null);
739
- const result = await dataProvider.getList<T>(resource, JSON.parse(paramsStr));
758
+ const result = await dataProvider.getList<T>(
759
+ resource,
760
+ JSON.parse(paramsStr),
761
+ );
740
762
  setData(result.data || []);
741
763
  setTotal(result.total || 0);
742
764
  } catch (err) {
@@ -747,40 +769,40 @@ export function useList<T = any>(
747
769
  setLoading(false);
748
770
  }
749
771
  }, [dataProvider, resource, paramsStr, enabled]);
750
-
772
+
751
773
  useEffect(() => {
752
774
  refetch();
753
775
  }, [refetch]);
754
-
776
+
755
777
  useEffect(() => {
756
778
  if (refetchInterval && enabled) {
757
779
  const interval = setInterval(refetch, refetchInterval);
758
780
  return () => clearInterval(interval);
759
781
  }
760
782
  }, [refetchInterval, refetch, enabled]);
761
-
783
+
762
784
  return { data: data || [], total, loading, error, refetch };
763
785
  }
764
786
 
765
787
  export function useOne<T = any>(
766
788
  resource: string,
767
789
  id: string | number | null | undefined,
768
- options: UseOneOptions = {}
790
+ options: UseOneOptions = {},
769
791
  ) {
770
792
  const [data, setData] = useState<T | null>(null);
771
793
  const [loading, setLoading] = useState<boolean>(true);
772
794
  const [error, setError] = useState<DataProviderError | null>(null);
773
795
 
774
796
  const dataProvider = useDataProvider();
775
-
797
+
776
798
  const { enabled = true } = options;
777
-
799
+
778
800
  const refetch = useCallback(async () => {
779
801
  if (!enabled || !id) {
780
802
  setLoading(false);
781
803
  return;
782
804
  }
783
-
805
+
784
806
  try {
785
807
  setLoading(true);
786
808
  setError(null);
@@ -793,73 +815,81 @@ export function useOne<T = any>(
793
815
  setLoading(false);
794
816
  }
795
817
  }, [dataProvider, resource, id, enabled]);
796
-
818
+
797
819
  useEffect(() => {
798
820
  refetch();
799
821
  }, [refetch]);
800
-
822
+
801
823
  return { data, loading, error, refetch };
802
824
  }
803
825
 
804
826
  export function useCreate<T = any, V = any>(
805
827
  resource: string,
806
828
  options: UseMutationOptions<T>,
807
- meta?:AxiosRequestConfig
829
+ meta?: AxiosRequestConfig,
808
830
  ) {
809
831
  const [loading, setLoading] = useState<boolean>(false);
810
832
  const [error, setError] = useState<DataProviderError | null>(null);
811
-
833
+
812
834
  const dataProvider = useDataProvider();
813
835
 
814
836
  const { onSuccess, onError } = options;
815
-
816
- const mutate = useCallback(async (variables: V): Promise<T> => {
817
- setLoading(true);
818
- setError(null);
819
-
820
- try {
821
- const result = await dataProvider.create<T, V>(resource, {
822
- variables,
823
- meta,
824
- });
825
- if (onSuccess) {
826
- onSuccess(result.data);
827
- }
828
- return result.data;
829
- } catch (err) {
830
- const errorObj = err as DataProviderError;
831
- setError(errorObj);
832
- if (onError) {
833
- onError(errorObj);
837
+
838
+ const mutate = useCallback(
839
+ async (variables: V): Promise<T | undefined> => {
840
+ setLoading(true);
841
+ setError(null);
842
+
843
+ try {
844
+ const result = await dataProvider.create<T, V>(resource, {
845
+ variables,
846
+ meta,
847
+ });
848
+ if (onSuccess) {
849
+ onSuccess(result.data);
850
+ }
851
+ return result.data;
852
+ } catch (err) {
853
+ const errorObj = err as DataProviderError;
854
+ setError(errorObj);
855
+ onError?.(errorObj);
856
+ return;
857
+ } finally {
858
+ setLoading(false);
834
859
  }
835
- throw errorObj;
836
- } finally {
837
- setLoading(false);
838
- }
839
- }, [dataProvider, resource, onSuccess, onError]);
840
-
860
+ },
861
+ [dataProvider, resource, onSuccess, onError],
862
+ );
863
+
841
864
  return { mutate, loading, error };
842
865
  }
843
866
 
844
867
  export function useUpdate<T = any, V = any>(
845
868
  resource: string,
846
869
  options: UseMutationOptions<T>,
847
- meta?: AxiosRequestConfig
870
+ meta?: AxiosRequestConfig,
848
871
  ) {
849
872
  const [loading, setLoading] = useState<boolean>(false);
850
873
  const [error, setError] = useState<DataProviderError | null>(null);
851
874
 
852
875
  const dataProvider = useDataProvider();
853
-
876
+
854
877
  const { onSuccess, onError } = options;
855
-
878
+
856
879
  const mutate = useCallback(
857
- async (id: string | number, variables: Partial<V>): Promise<T> => {
880
+ async (
881
+ id: string | number,
882
+ variables: Partial<V>,
883
+ ): Promise<T | undefined> => {
858
884
  setLoading(true);
859
885
  setError(null);
860
-
886
+
861
887
  try {
862
- const result = await dataProvider.update<T, V>(resource, { id, variables, meta });
888
+ const result = await dataProvider.update<T, V>(resource, {
889
+ id,
890
+ variables,
891
+ meta,
892
+ });
863
893
  if (onSuccess) {
864
894
  onSuccess(result.data);
865
895
  }
@@ -867,37 +897,35 @@ export function useUpdate<T = any, V = any>(
867
897
  } catch (err) {
868
898
  const errorObj = err as DataProviderError;
869
899
  setError(errorObj);
870
- if (onError) {
871
- onError(errorObj);
872
- }
873
- throw errorObj;
900
+ onError?.(errorObj);
901
+ return;
874
902
  } finally {
875
903
  setLoading(false);
876
904
  }
877
- },
878
- [dataProvider, resource, onSuccess, onError]
905
+ },
906
+ [dataProvider, resource, onSuccess, onError],
879
907
  );
880
-
908
+
881
909
  return { mutate, loading, error };
882
910
  }
883
911
 
884
912
  export function useDelete<T = any>(
885
913
  resource: string,
886
914
  options: UseMutationOptions<T>,
887
- meta?: AxiosRequestConfig
915
+ meta?: AxiosRequestConfig,
888
916
  ) {
889
917
  const [loading, setLoading] = useState<boolean>(false);
890
918
  const [error, setError] = useState<DataProviderError | null>(null);
891
919
 
892
920
  const dataProvider = useDataProvider();
893
-
921
+
894
922
  const { onSuccess, onError } = options;
895
-
923
+
896
924
  const mutate = useCallback(
897
- async (id: string | number): Promise<T> => {
925
+ async (id: string | number): Promise<T | undefined> => {
898
926
  setLoading(true);
899
927
  setError(null);
900
-
928
+
901
929
  try {
902
930
  const result = await dataProvider.deleteOne<T>(resource, { id, meta });
903
931
  if (onSuccess) {
@@ -907,17 +935,15 @@ export function useDelete<T = any>(
907
935
  } catch (err) {
908
936
  const errorObj = err as DataProviderError;
909
937
  setError(errorObj);
910
- if (onError) {
911
- onError(errorObj);
912
- }
913
- throw errorObj;
938
+ onError?.(errorObj);
939
+ return;
914
940
  } finally {
915
941
  setLoading(false);
916
942
  }
917
- },
918
- [dataProvider, resource, onSuccess, onError]
943
+ },
944
+ [dataProvider, resource, onSuccess, onError],
919
945
  );
920
-
946
+
921
947
  return { mutate, loading, error };
922
948
  }
923
949
 
@@ -934,7 +960,7 @@ export function useCustom<T = any>(
934
960
  const { onSuccess, onError } = options;
935
961
 
936
962
  const mutate = useCallback(
937
- async (variables?: Payload): Promise<T> => {
963
+ async (variables?: Payload): Promise<T | undefined> => {
938
964
  setLoading(true);
939
965
  setError(null);
940
966
 
@@ -945,7 +971,7 @@ export function useCustom<T = any>(
945
971
  method: options.method,
946
972
  headers: options.headers,
947
973
  query: options.query,
948
- ...options
974
+ ...options,
949
975
  });
950
976
  if (onSuccess) {
951
977
  onSuccess(result.data);
@@ -955,15 +981,13 @@ export function useCustom<T = any>(
955
981
  } catch (err) {
956
982
  const errorObj = err as DataProviderError;
957
983
  setError(errorObj);
958
- if (onError) {
959
- onError(errorObj);
960
- }
961
- throw errorObj;
984
+ onError?.(errorObj);
985
+ return;
962
986
  } finally {
963
987
  setLoading(false);
964
988
  }
965
989
  },
966
- [dataProvider, resource, onSuccess, onError]
990
+ [dataProvider, resource, onSuccess, onError],
967
991
  );
968
992
 
969
993
  return { mutate, loading, error, data: customData };
@@ -986,7 +1010,7 @@ export const useDataProvider = () => {
986
1010
  const ctx = useContext(DataProviderContext);
987
1011
  if (!ctx) {
988
1012
  throw new Error(
989
- "useDataProvider must be used inside <DataProviderContainer />"
1013
+ "useDataProvider must be used inside <DataProviderContainer />",
990
1014
  );
991
1015
  }
992
1016
  return ctx;
@@ -1004,27 +1028,29 @@ export const useDataProvider = () => {
1004
1028
  */
1005
1029
 
1006
1030
  export interface ICreateHttpClientOptions {
1007
- tokenName?: string ;
1031
+ tokenName?: string;
1008
1032
  tokenStorage?: "local" | "session" | "cookie" | "http-only";
1009
1033
  authorizationType?: "Bearer" | "Basic" | string;
1010
1034
  withCredentials?: boolean;
1011
1035
  }
1012
1036
 
1013
- export interface ICreateHttpClient {
1037
+ export interface ICreateHttpClient {
1014
1038
  url?: string;
1015
- options?: ICreateHttpClientOptions
1039
+ options?: ICreateHttpClientOptions;
1016
1040
  }
1017
- export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosInstance {
1041
+ export function createHttpClient({
1042
+ url,
1043
+ options = {},
1044
+ }: ICreateHttpClient): AxiosInstance {
1018
1045
  const {
1019
1046
  tokenName = "token",
1020
1047
  tokenStorage = "http-only",
1021
1048
  authorizationType = "Bearer",
1022
1049
  } = options;
1023
-
1050
+
1024
1051
  const withCredentials =
1025
1052
  options.withCredentials ?? tokenStorage === "http-only";
1026
1053
 
1027
-
1028
1054
  const axiosInstance = axios.create({
1029
1055
  baseURL: url || "https://api.example.com",
1030
1056
  withCredentials,
@@ -1047,8 +1073,8 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
1047
1073
  break;
1048
1074
 
1049
1075
  case "http-only":
1050
- // NO READ
1051
- // HttpOnly cookies are not accessible via JavaScript
1076
+ // NO READ
1077
+ // HttpOnly cookies are not accessible via JavaScript
1052
1078
  // Browser will send it automatically
1053
1079
  break;
1054
1080
  }
@@ -1056,7 +1082,7 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
1056
1082
  // Just set token if available in storage
1057
1083
  if (token) {
1058
1084
  config.headers.Authorization = `${authorizationType} ${token}`;
1059
- }
1085
+ }
1060
1086
 
1061
1087
  return config;
1062
1088
  });
@@ -1066,7 +1092,6 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
1066
1092
 
1067
1093
  // ================================ AUTH ================================
1068
1094
 
1069
-
1070
1095
  export interface AuthUser {
1071
1096
  id: string | number;
1072
1097
  email?: string;
@@ -1134,7 +1159,7 @@ export const useAuth = () => {
1134
1159
  export const AuthProvider: React.FC<AuthProviderProps> = ({
1135
1160
  children,
1136
1161
  urls,
1137
- tokenKey = 'token',
1162
+ tokenKey = "token",
1138
1163
  keysCleanUpOnLogout = ["token"],
1139
1164
  }) => {
1140
1165
  const dataProvider = useDataProvider();
@@ -1148,7 +1173,8 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1148
1173
  sessionStorage.removeItem(key);
1149
1174
  }
1150
1175
 
1151
- const login = useCallback(async (payload: LoginPayload, type: TypeResponse = "full") => {
1176
+ const login = useCallback(
1177
+ async (payload: LoginPayload, type: TypeResponse = "full") => {
1152
1178
  try {
1153
1179
  const res = await dataProvider.custom<any>({
1154
1180
  url: loginUrl,
@@ -1161,39 +1187,43 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1161
1187
  }
1162
1188
  return res;
1163
1189
  } catch (error) {
1164
- throw error;
1190
+ if (error instanceof Error) return Promise.reject(error);
1191
+ return Promise.reject(error);
1165
1192
  }
1166
- }
1167
- , [dataProvider, loginUrl]);
1193
+ },
1194
+ [dataProvider, loginUrl],
1195
+ );
1168
1196
 
1169
- const logout = useCallback(async (params: CustomParams, type: TypeResponse = "full") => {
1197
+ const logout = useCallback(
1198
+ async (params: CustomParams, type: TypeResponse = "full") => {
1199
+ try {
1200
+ const res = await dataProvider.custom<any>({
1201
+ url: params.url || logoutUrl,
1202
+ ...params,
1203
+ });
1170
1204
 
1171
- try {
1172
- const res = await dataProvider.custom<any>({
1173
- url: params.url || logoutUrl,
1174
- ...params,
1175
- });
1205
+ if (type === "simple") {
1206
+ return res.data;
1207
+ }
1176
1208
 
1177
- if (type === "simple") {
1178
- return res.data;
1179
- }
1180
-
1181
- return res;
1182
- } catch (error) {
1183
- throw error;
1184
- } finally {
1185
- setUser(null);
1186
- dataProvider.clearAllCache();
1187
- if(Array.isArray(keysCleanUpOnLogout)){
1188
- keysCleanUpOnLogout.forEach((key) => {
1189
- removeKeys(key);
1190
- });
1191
- } else {
1192
- removeKeys(keysCleanUpOnLogout);
1209
+ return res;
1210
+ } catch (error) {
1211
+ if (error instanceof Error) return Promise.reject(error);
1212
+ return Promise.reject(error);
1213
+ } finally {
1214
+ setUser(null);
1215
+ dataProvider.clearAllCache();
1216
+ if (Array.isArray(keysCleanUpOnLogout)) {
1217
+ keysCleanUpOnLogout.forEach((key) => {
1218
+ removeKeys(key);
1219
+ });
1220
+ } else {
1221
+ removeKeys(keysCleanUpOnLogout);
1222
+ }
1193
1223
  }
1194
- }
1195
-
1196
- }, [dataProvider, logoutUrl]);
1224
+ },
1225
+ [dataProvider, logoutUrl],
1226
+ );
1197
1227
 
1198
1228
  const isAuthenticated = () => {
1199
1229
  return user !== null;
@@ -1202,27 +1232,29 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1202
1232
  return cookiesProvider.get(tokenKey);
1203
1233
  }, [tokenKey]);
1204
1234
 
1205
- const getMe = useCallback(async (type?: TypeResponse) => {
1206
- try {
1207
- const res = await dataProvider.custom<AuthUser>({
1208
- url: meUrl,
1209
- method: 'get',
1210
- });
1235
+ const getMe = useCallback(
1236
+ async (type?: TypeResponse) => {
1237
+ try {
1238
+ const res = await dataProvider.custom<AuthUser>({
1239
+ url: meUrl,
1240
+ method: "get",
1241
+ });
1211
1242
 
1212
- if (type === "simple") {
1213
- return res.data;
1243
+ if (type === "simple") {
1244
+ return res.data;
1245
+ }
1246
+ return res;
1247
+ } catch {
1248
+ return null;
1214
1249
  }
1215
- return res;
1216
- } catch {
1217
- return null;
1218
- }
1219
- }, [dataProvider, meUrl]);
1250
+ },
1251
+ [dataProvider, meUrl],
1252
+ );
1220
1253
 
1221
1254
  useEffect(() => {
1222
1255
  getMe().then((u: any) => u && setUser(u));
1223
1256
  }, [getMe]);
1224
1257
 
1225
-
1226
1258
  const value: AuthContextValue = {
1227
1259
  user,
1228
1260
  setUser,
@@ -1232,7 +1264,6 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1232
1264
  logout,
1233
1265
  getMe,
1234
1266
  };
1235
-
1236
1267
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
1237
1268
  };
1238
1269
 
@@ -1251,33 +1282,29 @@ export const cookiesProvider = {
1251
1282
  },
1252
1283
 
1253
1284
  remove: (name: string) => {
1254
- Cookies.remove(name, {path: "/"});
1285
+ Cookies.remove(name, { path: "/" });
1255
1286
  },
1256
1287
 
1257
-
1258
1288
  exists: (name: string): boolean => {
1259
1289
  return Cookies.get(name) !== undefined;
1260
1290
  },
1261
1291
  };
1262
1292
 
1263
-
1264
-
1265
1293
  // =================== Example ===================
1266
1294
 
1267
1295
  // create httpClient - (can create multiple httpClients for different apis)
1268
1296
 
1269
1297
  // const TOKEN = "token";
1270
1298
 
1271
- // const httpClient = createHttpClient({
1272
- // url: `${process.env.NEXT_PUBLIC_API_URL}`,
1273
- // options: {
1274
- // authorizationType: "Bearer",
1275
- // tokenName: TOKEN,
1276
- // tokenStorage: "cookie",
1277
- // withCredentials: true, --- optionals (default to true if tokenStorage is "http-only")
1278
- // },
1279
- // });
1280
-
1299
+ // const httpClient = createHttpClient({
1300
+ // url: `${process.env.NEXT_PUBLIC_API_URL}`,
1301
+ // options: {
1302
+ // authorizationType: "Bearer",
1303
+ // tokenName: TOKEN,
1304
+ // tokenStorage: "cookie",
1305
+ // withCredentials: true, --- optionals (default to true if tokenStorage is "http-only")
1306
+ // },
1307
+ // });
1281
1308
 
1282
1309
  // create dataProvider
1283
1310
 
@@ -1293,16 +1320,15 @@ export const cookiesProvider = {
1293
1320
 
1294
1321
  // wrapped all into:
1295
1322
  // <DataProvider dataProvider={dataProvider}>
1296
- // <AuthProvider
1297
- // urls={urls} --> api_login
1298
- // tokenKey={TOKEN}
1323
+ // <AuthProvider
1324
+ // urls={urls} --> api_login
1325
+ // tokenKey={TOKEN}
1299
1326
  // keysCleanUpOnLogout={keysRemoveOnLogout} --> optional (default to ["token"]) - additional keys to clean up on logout
1300
1327
  // >
1301
1328
  // <App />
1302
1329
  // </AuthProvider>
1303
1330
  // </DataProvider>
1304
1331
 
1305
-
1306
1332
  // use hooks to auth
1307
1333
 
1308
1334
  // const { login, logout, refresh } = useAuth();
@@ -1338,4 +1364,4 @@ export const cookiesProvider = {
1338
1364
  // url: '/route_name or api_url/route/...',
1339
1365
  // method: 'post',
1340
1366
  // payload: {},
1341
- // });
1367
+ // });