api-core-lib 1.0.0 → 1.1.0
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/dist/index.d.mts +81 -47
- package/dist/index.d.ts +81 -47
- package/dist/index.js +100 -182
- package/dist/index.mjs +100 -179
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
@@ -1,74 +1,103 @@
|
|
1
|
-
import { AxiosRequestConfig, AxiosInstance } from 'axios';
|
1
|
+
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance } from 'axios';
|
2
2
|
import * as react from 'react';
|
3
3
|
|
4
|
+
/**
|
5
|
+
* @file src/types.ts
|
6
|
+
* @description
|
7
|
+
* هذا الملف هو المصدر المركزي لجميع أنواع البيانات (Types) والواجهات (Interfaces)
|
8
|
+
* المستخدمة في المكتبة. تعريف الأنواع هنا يضمن التناسق وسلامة الأنواع
|
9
|
+
* في جميع أنحاء المشروع ويوفر واجهة واضحة للمستخدم النهائي.
|
10
|
+
*/
|
11
|
+
|
12
|
+
/**
|
13
|
+
* يمثل بيانات الترقيم (Pagination) التي تأتي من الـ API.
|
14
|
+
*/
|
4
15
|
interface PaginationMeta {
|
5
16
|
itemsPerPage: number;
|
6
17
|
totalItems: number;
|
7
18
|
currentPage: number;
|
8
19
|
totalPages: number;
|
9
|
-
sortBy?: [string, 'asc' | 'desc'][];
|
10
|
-
}
|
11
|
-
interface ApiLinks {
|
12
|
-
current: string;
|
13
|
-
first?: string;
|
14
|
-
last?: string;
|
15
|
-
next?: string;
|
16
|
-
prev?: string;
|
17
20
|
}
|
21
|
+
/**
|
22
|
+
* يمثل بنية الاستجابة القياسية من الـ API.
|
23
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
24
|
+
*/
|
18
25
|
interface ApiResponse<T = any> {
|
19
26
|
data: T;
|
20
|
-
meta?: PaginationMeta;
|
21
|
-
links?: ApiLinks;
|
22
27
|
message?: string;
|
23
|
-
|
28
|
+
meta?: PaginationMeta;
|
24
29
|
}
|
30
|
+
/**
|
31
|
+
* يمثل خطأ التحقق من صحة حقل معين.
|
32
|
+
*/
|
25
33
|
interface ValidationError {
|
26
34
|
field: string;
|
27
35
|
message: string;
|
28
|
-
code: string;
|
29
36
|
}
|
37
|
+
/**
|
38
|
+
* يمثل كائن الخطأ الموحد الذي تنتجه المكتبة.
|
39
|
+
*/
|
30
40
|
interface ApiError {
|
31
41
|
message: string;
|
32
42
|
status: number;
|
33
43
|
code?: string;
|
34
44
|
errors?: ValidationError[];
|
35
|
-
timestamp?: string;
|
36
|
-
path?: string;
|
37
45
|
requestId?: string;
|
38
46
|
}
|
47
|
+
/**
|
48
|
+
* يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
|
49
|
+
* @template T نوع البيانات المتوقعة في حالة النجاح.
|
50
|
+
*/
|
39
51
|
interface StandardResponse<T> {
|
40
52
|
response?: ApiResponse<T> | T;
|
41
53
|
error: ApiError | null;
|
42
54
|
loading: boolean;
|
43
55
|
success: boolean;
|
44
56
|
message?: string;
|
45
|
-
cached?: boolean;
|
46
57
|
validationErrors?: ValidationError[];
|
47
58
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
59
|
+
/**
|
60
|
+
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
61
|
+
*/
|
62
|
+
interface Tokens {
|
63
|
+
accessToken: string | null;
|
64
|
+
refreshToken: string | null;
|
65
|
+
expiresAt?: number;
|
66
|
+
tokenType?: string;
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* واجهة لمدير التوكنات، تسمح بفصل منطق تخزين التوكن.
|
70
|
+
* يمكن للمستخدم توفير تطبيق لهذه الواجهة (e.g., LocalStorage, Cookies).
|
71
|
+
*/
|
72
|
+
interface TokenManager {
|
73
|
+
getTokens(): Promise<Tokens>;
|
74
|
+
setTokens(tokens: Tokens): Promise<void>;
|
75
|
+
clearTokens(): Promise<void>;
|
76
|
+
isHttpOnly(): boolean;
|
51
77
|
}
|
78
|
+
/**
|
79
|
+
* يوسع إعدادات طلب Axios القياسية بخيارات خاصة بالمكتبة.
|
80
|
+
*/
|
52
81
|
interface RequestConfig extends AxiosRequestConfig {
|
53
|
-
retry?: boolean;
|
54
|
-
retryCount?: number;
|
55
|
-
cache?: boolean | CacheConfig;
|
56
82
|
cancelTokenKey?: string;
|
83
|
+
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
84
|
+
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
57
85
|
}
|
86
|
+
/**
|
87
|
+
* الواجهة الرئيسية لتهيئة عميل الـ API.
|
88
|
+
*/
|
58
89
|
interface ApiClientConfig {
|
59
90
|
baseURL: string;
|
91
|
+
tokenManager: TokenManager;
|
60
92
|
timeout?: number;
|
61
93
|
headers?: Record<string, string>;
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
newAccessToken: string;
|
66
|
-
newRefreshToken: string;
|
67
|
-
}>;
|
68
|
-
onTokenRefreshSuccess?: (newAccessToken: string, newRefreshToken: string) => void;
|
69
|
-
onTokenRefreshError?: (error: any) => void;
|
94
|
+
withCredentials?: boolean;
|
95
|
+
responseType?: ResponseType;
|
96
|
+
onRefreshError?: (error: any) => void;
|
70
97
|
}
|
71
|
-
|
98
|
+
/**
|
99
|
+
* يمثل خيارات الاستعلام للترقيم والبحث والفرز.
|
100
|
+
*/
|
72
101
|
interface PaginateQueryOptions {
|
73
102
|
page?: number;
|
74
103
|
limit?: number;
|
@@ -78,31 +107,30 @@ interface PaginateQueryOptions {
|
|
78
107
|
direction: 'asc' | 'desc';
|
79
108
|
}[];
|
80
109
|
filter?: Record<string, any>;
|
81
|
-
[key: string]: any;
|
82
110
|
}
|
111
|
+
/**
|
112
|
+
* واجهة لتهيئة الهوك `useApi`.
|
113
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
114
|
+
*/
|
83
115
|
interface UseApiConfig<T> {
|
84
116
|
endpoint: string;
|
85
|
-
reduxKey?: string;
|
86
117
|
initialData?: T | T[];
|
87
118
|
initialQuery?: PaginateQueryOptions;
|
88
119
|
enabled?: boolean;
|
89
120
|
refetchAfterChange?: boolean;
|
90
|
-
isToastAfterActions?: boolean;
|
91
121
|
onSuccess?: (message: string, data?: T) => void;
|
92
122
|
onError?: (message: string, error?: ApiError) => void;
|
93
123
|
}
|
94
124
|
|
95
|
-
|
125
|
+
/**
|
126
|
+
* @file src/core/client.ts
|
127
|
+
* @description
|
128
|
+
* هذا هو القلب النابض للمكتبة. دالة `createApiClient` تنشئ وتهيئ نسخة Axios
|
129
|
+
* مع معترضات (interceptors) ذكية لإدارة التوكنات، بما في ذلك التجديد التلقائي
|
130
|
+
* والاستباقي للتوكن، ومعالجة الأخطاء بشكل مركزي.
|
131
|
+
*/
|
96
132
|
|
97
|
-
declare
|
98
|
-
private cache;
|
99
|
-
private defaultDuration;
|
100
|
-
set<T>(key: string, data: T, duration?: number): void;
|
101
|
-
get<T>(key: string): T | null;
|
102
|
-
delete(key: string): void;
|
103
|
-
clear(): void;
|
104
|
-
}
|
105
|
-
declare const cacheManager: CacheManager;
|
133
|
+
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
106
134
|
|
107
135
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
108
136
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
@@ -112,8 +140,14 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
112
140
|
remove: (id: string, config?: RequestConfig) => Promise<StandardResponse<any>>;
|
113
141
|
};
|
114
142
|
|
115
|
-
|
116
|
-
|
143
|
+
/**
|
144
|
+
* @file src/core/utils.ts
|
145
|
+
* @description
|
146
|
+
* يحتوي هذا الملف على مجموعة من الدوال المساعدة المستخدمة في جميع أنحاء المكتبة،
|
147
|
+
* مثل بناء سلاسل الاستعلام (query strings) وإدارة إلغاء الطلبات،
|
148
|
+
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
149
|
+
*/
|
150
|
+
|
117
151
|
declare function buildPaginateQuery(query: PaginateQueryOptions): string;
|
118
152
|
|
119
153
|
declare function useApi<T extends {
|
@@ -142,4 +176,4 @@ declare function useApi<T extends {
|
|
142
176
|
};
|
143
177
|
};
|
144
178
|
|
145
|
-
export { type ApiClientConfig, type ApiError, type
|
179
|
+
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, createApiClient, createApiServices, useApi };
|
package/dist/index.d.ts
CHANGED
@@ -1,74 +1,103 @@
|
|
1
|
-
import { AxiosRequestConfig, AxiosInstance } from 'axios';
|
1
|
+
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance } from 'axios';
|
2
2
|
import * as react from 'react';
|
3
3
|
|
4
|
+
/**
|
5
|
+
* @file src/types.ts
|
6
|
+
* @description
|
7
|
+
* هذا الملف هو المصدر المركزي لجميع أنواع البيانات (Types) والواجهات (Interfaces)
|
8
|
+
* المستخدمة في المكتبة. تعريف الأنواع هنا يضمن التناسق وسلامة الأنواع
|
9
|
+
* في جميع أنحاء المشروع ويوفر واجهة واضحة للمستخدم النهائي.
|
10
|
+
*/
|
11
|
+
|
12
|
+
/**
|
13
|
+
* يمثل بيانات الترقيم (Pagination) التي تأتي من الـ API.
|
14
|
+
*/
|
4
15
|
interface PaginationMeta {
|
5
16
|
itemsPerPage: number;
|
6
17
|
totalItems: number;
|
7
18
|
currentPage: number;
|
8
19
|
totalPages: number;
|
9
|
-
sortBy?: [string, 'asc' | 'desc'][];
|
10
|
-
}
|
11
|
-
interface ApiLinks {
|
12
|
-
current: string;
|
13
|
-
first?: string;
|
14
|
-
last?: string;
|
15
|
-
next?: string;
|
16
|
-
prev?: string;
|
17
20
|
}
|
21
|
+
/**
|
22
|
+
* يمثل بنية الاستجابة القياسية من الـ API.
|
23
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
24
|
+
*/
|
18
25
|
interface ApiResponse<T = any> {
|
19
26
|
data: T;
|
20
|
-
meta?: PaginationMeta;
|
21
|
-
links?: ApiLinks;
|
22
27
|
message?: string;
|
23
|
-
|
28
|
+
meta?: PaginationMeta;
|
24
29
|
}
|
30
|
+
/**
|
31
|
+
* يمثل خطأ التحقق من صحة حقل معين.
|
32
|
+
*/
|
25
33
|
interface ValidationError {
|
26
34
|
field: string;
|
27
35
|
message: string;
|
28
|
-
code: string;
|
29
36
|
}
|
37
|
+
/**
|
38
|
+
* يمثل كائن الخطأ الموحد الذي تنتجه المكتبة.
|
39
|
+
*/
|
30
40
|
interface ApiError {
|
31
41
|
message: string;
|
32
42
|
status: number;
|
33
43
|
code?: string;
|
34
44
|
errors?: ValidationError[];
|
35
|
-
timestamp?: string;
|
36
|
-
path?: string;
|
37
45
|
requestId?: string;
|
38
46
|
}
|
47
|
+
/**
|
48
|
+
* يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
|
49
|
+
* @template T نوع البيانات المتوقعة في حالة النجاح.
|
50
|
+
*/
|
39
51
|
interface StandardResponse<T> {
|
40
52
|
response?: ApiResponse<T> | T;
|
41
53
|
error: ApiError | null;
|
42
54
|
loading: boolean;
|
43
55
|
success: boolean;
|
44
56
|
message?: string;
|
45
|
-
cached?: boolean;
|
46
57
|
validationErrors?: ValidationError[];
|
47
58
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
59
|
+
/**
|
60
|
+
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
61
|
+
*/
|
62
|
+
interface Tokens {
|
63
|
+
accessToken: string | null;
|
64
|
+
refreshToken: string | null;
|
65
|
+
expiresAt?: number;
|
66
|
+
tokenType?: string;
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* واجهة لمدير التوكنات، تسمح بفصل منطق تخزين التوكن.
|
70
|
+
* يمكن للمستخدم توفير تطبيق لهذه الواجهة (e.g., LocalStorage, Cookies).
|
71
|
+
*/
|
72
|
+
interface TokenManager {
|
73
|
+
getTokens(): Promise<Tokens>;
|
74
|
+
setTokens(tokens: Tokens): Promise<void>;
|
75
|
+
clearTokens(): Promise<void>;
|
76
|
+
isHttpOnly(): boolean;
|
51
77
|
}
|
78
|
+
/**
|
79
|
+
* يوسع إعدادات طلب Axios القياسية بخيارات خاصة بالمكتبة.
|
80
|
+
*/
|
52
81
|
interface RequestConfig extends AxiosRequestConfig {
|
53
|
-
retry?: boolean;
|
54
|
-
retryCount?: number;
|
55
|
-
cache?: boolean | CacheConfig;
|
56
82
|
cancelTokenKey?: string;
|
83
|
+
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
84
|
+
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
57
85
|
}
|
86
|
+
/**
|
87
|
+
* الواجهة الرئيسية لتهيئة عميل الـ API.
|
88
|
+
*/
|
58
89
|
interface ApiClientConfig {
|
59
90
|
baseURL: string;
|
91
|
+
tokenManager: TokenManager;
|
60
92
|
timeout?: number;
|
61
93
|
headers?: Record<string, string>;
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
newAccessToken: string;
|
66
|
-
newRefreshToken: string;
|
67
|
-
}>;
|
68
|
-
onTokenRefreshSuccess?: (newAccessToken: string, newRefreshToken: string) => void;
|
69
|
-
onTokenRefreshError?: (error: any) => void;
|
94
|
+
withCredentials?: boolean;
|
95
|
+
responseType?: ResponseType;
|
96
|
+
onRefreshError?: (error: any) => void;
|
70
97
|
}
|
71
|
-
|
98
|
+
/**
|
99
|
+
* يمثل خيارات الاستعلام للترقيم والبحث والفرز.
|
100
|
+
*/
|
72
101
|
interface PaginateQueryOptions {
|
73
102
|
page?: number;
|
74
103
|
limit?: number;
|
@@ -78,31 +107,30 @@ interface PaginateQueryOptions {
|
|
78
107
|
direction: 'asc' | 'desc';
|
79
108
|
}[];
|
80
109
|
filter?: Record<string, any>;
|
81
|
-
[key: string]: any;
|
82
110
|
}
|
111
|
+
/**
|
112
|
+
* واجهة لتهيئة الهوك `useApi`.
|
113
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
114
|
+
*/
|
83
115
|
interface UseApiConfig<T> {
|
84
116
|
endpoint: string;
|
85
|
-
reduxKey?: string;
|
86
117
|
initialData?: T | T[];
|
87
118
|
initialQuery?: PaginateQueryOptions;
|
88
119
|
enabled?: boolean;
|
89
120
|
refetchAfterChange?: boolean;
|
90
|
-
isToastAfterActions?: boolean;
|
91
121
|
onSuccess?: (message: string, data?: T) => void;
|
92
122
|
onError?: (message: string, error?: ApiError) => void;
|
93
123
|
}
|
94
124
|
|
95
|
-
|
125
|
+
/**
|
126
|
+
* @file src/core/client.ts
|
127
|
+
* @description
|
128
|
+
* هذا هو القلب النابض للمكتبة. دالة `createApiClient` تنشئ وتهيئ نسخة Axios
|
129
|
+
* مع معترضات (interceptors) ذكية لإدارة التوكنات، بما في ذلك التجديد التلقائي
|
130
|
+
* والاستباقي للتوكن، ومعالجة الأخطاء بشكل مركزي.
|
131
|
+
*/
|
96
132
|
|
97
|
-
declare
|
98
|
-
private cache;
|
99
|
-
private defaultDuration;
|
100
|
-
set<T>(key: string, data: T, duration?: number): void;
|
101
|
-
get<T>(key: string): T | null;
|
102
|
-
delete(key: string): void;
|
103
|
-
clear(): void;
|
104
|
-
}
|
105
|
-
declare const cacheManager: CacheManager;
|
133
|
+
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
106
134
|
|
107
135
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
108
136
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
@@ -112,8 +140,14 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
112
140
|
remove: (id: string, config?: RequestConfig) => Promise<StandardResponse<any>>;
|
113
141
|
};
|
114
142
|
|
115
|
-
|
116
|
-
|
143
|
+
/**
|
144
|
+
* @file src/core/utils.ts
|
145
|
+
* @description
|
146
|
+
* يحتوي هذا الملف على مجموعة من الدوال المساعدة المستخدمة في جميع أنحاء المكتبة،
|
147
|
+
* مثل بناء سلاسل الاستعلام (query strings) وإدارة إلغاء الطلبات،
|
148
|
+
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
149
|
+
*/
|
150
|
+
|
117
151
|
declare function buildPaginateQuery(query: PaginateQueryOptions): string;
|
118
152
|
|
119
153
|
declare function useApi<T extends {
|
@@ -142,4 +176,4 @@ declare function useApi<T extends {
|
|
142
176
|
};
|
143
177
|
};
|
144
178
|
|
145
|
-
export { type ApiClientConfig, type ApiError, type
|
179
|
+
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, createApiClient, createApiServices, useApi };
|
package/dist/index.js
CHANGED
@@ -31,9 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
31
31
|
var index_exports = {};
|
32
32
|
__export(index_exports, {
|
33
33
|
buildPaginateQuery: () => buildPaginateQuery,
|
34
|
-
cacheManager: () => cacheManager,
|
35
|
-
cancelAllRequests: () => cancelAllRequests,
|
36
|
-
cancelRequest: () => cancelRequest,
|
37
34
|
createApiClient: () => createApiClient,
|
38
35
|
createApiServices: () => createApiServices,
|
39
36
|
useApi: () => useApi
|
@@ -41,200 +38,98 @@ __export(index_exports, {
|
|
41
38
|
module.exports = __toCommonJS(index_exports);
|
42
39
|
|
43
40
|
// src/core/client.ts
|
44
|
-
var
|
45
|
-
var import_axios_retry = __toESM(require("axios-retry"));
|
41
|
+
var import_axios = __toESM(require("axios"));
|
46
42
|
var import_uuid = require("uuid");
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
43
|
+
async function refreshToken(config, tokenManager) {
|
44
|
+
try {
|
45
|
+
const currentTokens = await tokenManager.getTokens();
|
46
|
+
if (!currentTokens.refreshToken) throw new Error("No refresh token available.");
|
47
|
+
const response = await import_axios.default.post(
|
48
|
+
`${config.baseURL}/auth/refresh`,
|
49
|
+
{ refresh_token: currentTokens.refreshToken },
|
50
|
+
{ withCredentials: config.withCredentials }
|
51
|
+
);
|
52
|
+
const { access_token, refresh_token, expires_in, token_type } = response.data;
|
53
|
+
const newTokens = {
|
54
|
+
accessToken: access_token,
|
55
|
+
refreshToken: refresh_token || currentTokens.refreshToken,
|
56
|
+
expiresAt: Date.now() + expires_in * 1e3,
|
57
|
+
tokenType: token_type || "Bearer"
|
58
|
+
};
|
59
|
+
await tokenManager.setTokens(newTokens);
|
60
|
+
console.log("[API Core] Tokens refreshed successfully.");
|
61
|
+
return newTokens;
|
62
|
+
} catch (err) {
|
63
|
+
console.error("[API Core] Failed to refresh token.", err);
|
64
|
+
if (config.onRefreshError) {
|
65
|
+
config.onRefreshError(err);
|
67
66
|
}
|
68
|
-
|
69
|
-
|
70
|
-
delete(key) {
|
71
|
-
this.cache.delete(key);
|
72
|
-
}
|
73
|
-
clear() {
|
74
|
-
this.cache.clear();
|
75
|
-
}
|
76
|
-
};
|
77
|
-
var cacheManager = new CacheManager();
|
78
|
-
|
79
|
-
// src/core/utils.ts
|
80
|
-
var import_axios = __toESM(require("axios"));
|
81
|
-
var cancelTokens = /* @__PURE__ */ new Map();
|
82
|
-
var createCancelToken = (key) => {
|
83
|
-
const existingToken = cancelTokens.get(key);
|
84
|
-
if (existingToken) {
|
85
|
-
existingToken.cancel("Operation canceled due to new request.");
|
86
|
-
cancelTokens.delete(key);
|
87
|
-
}
|
88
|
-
const source = import_axios.default.CancelToken.source();
|
89
|
-
cancelTokens.set(key, source);
|
90
|
-
return source;
|
91
|
-
};
|
92
|
-
var cancelRequest = (key) => {
|
93
|
-
const source = cancelTokens.get(key);
|
94
|
-
if (source) {
|
95
|
-
source.cancel("Operation canceled by user.");
|
96
|
-
cancelTokens.delete(key);
|
97
|
-
}
|
98
|
-
};
|
99
|
-
var cancelAllRequests = () => {
|
100
|
-
cancelTokens.forEach((source) => source.cancel("All operations canceled."));
|
101
|
-
cancelTokens.clear();
|
102
|
-
};
|
103
|
-
function buildPaginateQuery(query) {
|
104
|
-
const params = new URLSearchParams();
|
105
|
-
if (!query) return "";
|
106
|
-
if (query.page) params.append("page", query.page.toString());
|
107
|
-
if (query.limit) params.append("limit", query.limit.toString());
|
108
|
-
if (query.search) params.append("search", query.search);
|
109
|
-
if (query.sortBy) {
|
110
|
-
query.sortBy.forEach((sort) => params.append("sortBy", `${sort.key}:${sort.direction}`));
|
111
|
-
}
|
112
|
-
if (query.filter) {
|
113
|
-
Object.entries(query.filter).forEach(([field, value]) => {
|
114
|
-
params.append(`filter.${field}`, value);
|
115
|
-
});
|
67
|
+
await tokenManager.clearTokens();
|
68
|
+
return null;
|
116
69
|
}
|
117
|
-
const queryString = params.toString();
|
118
|
-
return queryString ? `?${queryString}` : "";
|
119
|
-
}
|
120
|
-
function isApiError(obj) {
|
121
|
-
return obj && typeof obj.status === "number" && typeof obj.message === "string" && obj.config === void 0;
|
122
70
|
}
|
123
|
-
function isAxiosResponse(obj) {
|
124
|
-
return obj && obj.data !== void 0 && obj.status !== void 0 && obj.config !== void 0;
|
125
|
-
}
|
126
|
-
|
127
|
-
// src/core/client.ts
|
128
71
|
function createApiClient(config) {
|
129
72
|
const {
|
130
73
|
baseURL,
|
131
|
-
|
74
|
+
tokenManager,
|
75
|
+
timeout = 15e3,
|
132
76
|
headers = {},
|
133
|
-
|
134
|
-
getRefreshToken,
|
135
|
-
handleTokenRefresh,
|
136
|
-
onTokenRefreshSuccess,
|
137
|
-
onTokenRefreshError
|
77
|
+
withCredentials = false
|
138
78
|
} = config;
|
139
|
-
const axiosInstance =
|
79
|
+
const axiosInstance = import_axios.default.create({
|
140
80
|
baseURL,
|
141
81
|
timeout,
|
142
|
-
headers: {
|
143
|
-
|
144
|
-
Accept: "application/json",
|
145
|
-
...headers
|
146
|
-
},
|
147
|
-
withCredentials: true
|
148
|
-
});
|
149
|
-
(0, import_axios_retry.default)(axiosInstance, {
|
150
|
-
retries: 3,
|
151
|
-
retryDelay: import_axios_retry.default.exponentialDelay,
|
152
|
-
retryCondition: (error) => import_axios_retry.default.isNetworkOrIdempotentRequestError(error) || error.response?.status === 429
|
82
|
+
headers: { "Content-Type": "application/json", ...headers },
|
83
|
+
withCredentials
|
153
84
|
});
|
154
|
-
let
|
155
|
-
let requestQueue = [];
|
85
|
+
let tokenRefreshPromise = null;
|
156
86
|
axiosInstance.interceptors.request.use(async (req) => {
|
157
87
|
req.headers["X-Request-ID"] = (0, import_uuid.v4)();
|
158
|
-
if (
|
159
|
-
|
160
|
-
|
161
|
-
req.headers.Authorization = `Bearer ${token}`;
|
162
|
-
}
|
88
|
+
if (req.url?.includes("/auth/")) return req;
|
89
|
+
if (tokenRefreshPromise) {
|
90
|
+
await tokenRefreshPromise;
|
163
91
|
}
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if (
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
console.log(`[Cache] HIT for ${cacheKey}`);
|
172
|
-
req.adapter = () => Promise.resolve({
|
173
|
-
data: cachedData,
|
174
|
-
status: 200,
|
175
|
-
statusText: "OK (from cache)",
|
176
|
-
headers: req.headers,
|
177
|
-
config: req
|
178
|
-
});
|
179
|
-
req.cached = true;
|
92
|
+
let tokens = await tokenManager.getTokens();
|
93
|
+
const now = Date.now();
|
94
|
+
const tokenBuffer = 60 * 1e3;
|
95
|
+
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
|
96
|
+
if (!tokenRefreshPromise) {
|
97
|
+
console.log("[API Core] Proactive token refresh initiated.");
|
98
|
+
tokenRefreshPromise = refreshToken(config, tokenManager);
|
180
99
|
}
|
100
|
+
const newTokens = await tokenRefreshPromise;
|
101
|
+
tokenRefreshPromise = null;
|
102
|
+
if (newTokens) tokens = newTokens;
|
103
|
+
}
|
104
|
+
if (tokens.accessToken && !tokenManager.isHttpOnly()) {
|
105
|
+
const tokenType = tokens.tokenType || "Bearer";
|
106
|
+
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
181
107
|
}
|
182
108
|
return req;
|
183
109
|
}, (error) => Promise.reject(error));
|
184
110
|
axiosInstance.interceptors.response.use(
|
185
|
-
(response) =>
|
186
|
-
const config2 = response.config;
|
187
|
-
if (response.config.method?.toLowerCase() === "get" && config2.cache) {
|
188
|
-
const cacheKey = response.config.url || "default-cache-key";
|
189
|
-
const cacheConfig = typeof config2.cache === "object" ? config2.cache : { enabled: true };
|
190
|
-
if (cacheConfig.enabled) {
|
191
|
-
console.log(`[Cache] SET for ${cacheKey}`);
|
192
|
-
cacheManager.set(cacheKey, response.data, cacheConfig.duration);
|
193
|
-
}
|
194
|
-
}
|
195
|
-
return response;
|
196
|
-
},
|
111
|
+
(response) => response,
|
197
112
|
async (error) => {
|
198
113
|
const originalRequest = error.config;
|
199
|
-
if (error.response?.status === 401 && !originalRequest._retry
|
114
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
200
115
|
originalRequest._retry = true;
|
201
|
-
if (
|
202
|
-
|
203
|
-
|
204
|
-
originalRequest.headers.Authorization = `Bearer ${token}`;
|
205
|
-
resolve(axiosInstance(originalRequest));
|
206
|
-
});
|
207
|
-
});
|
116
|
+
if (!tokenRefreshPromise) {
|
117
|
+
console.log("[API Core] Reactive token refresh initiated due to 401.");
|
118
|
+
tokenRefreshPromise = refreshToken(config, tokenManager);
|
208
119
|
}
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
if (
|
213
|
-
|
214
|
-
if (onTokenRefreshSuccess) {
|
215
|
-
onTokenRefreshSuccess(newAccessToken, newRefreshToken);
|
120
|
+
const newTokens = await tokenRefreshPromise;
|
121
|
+
tokenRefreshPromise = null;
|
122
|
+
if (newTokens) {
|
123
|
+
if (newTokens.accessToken && !tokenManager.isHttpOnly()) {
|
124
|
+
originalRequest.headers.Authorization = `${newTokens.tokenType || "Bearer"} ${newTokens.accessToken}`;
|
216
125
|
}
|
217
|
-
axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${newAccessToken}`;
|
218
|
-
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
219
|
-
requestQueue.forEach((cb) => cb(newAccessToken));
|
220
|
-
requestQueue = [];
|
221
126
|
return axiosInstance(originalRequest);
|
222
|
-
} catch (refreshError) {
|
223
|
-
if (onTokenRefreshError) {
|
224
|
-
onTokenRefreshError(refreshError);
|
225
|
-
}
|
226
|
-
requestQueue = [];
|
227
|
-
return Promise.reject(refreshError);
|
228
|
-
} finally {
|
229
|
-
isRefreshing = false;
|
230
127
|
}
|
231
128
|
}
|
232
129
|
const enhancedError = {
|
233
|
-
message: error.response?.data?.message || error.message
|
130
|
+
message: error.response?.data?.message || error.message,
|
234
131
|
status: error.response?.status || 500,
|
235
|
-
code: error.code,
|
236
132
|
errors: error.response?.data?.errors,
|
237
|
-
path: originalRequest.url,
|
238
133
|
requestId: originalRequest.headers?.["X-Request-ID"]
|
239
134
|
};
|
240
135
|
return Promise.reject(enhancedError);
|
@@ -243,6 +138,32 @@ function createApiClient(config) {
|
|
243
138
|
return axiosInstance;
|
244
139
|
}
|
245
140
|
|
141
|
+
// src/core/utils.ts
|
142
|
+
var import_axios2 = __toESM(require("axios"));
|
143
|
+
function buildPaginateQuery(query) {
|
144
|
+
if (!query) return "";
|
145
|
+
const params = new URLSearchParams();
|
146
|
+
if (query.page) params.append("page", query.page.toString());
|
147
|
+
if (query.limit) params.append("limit", query.limit.toString());
|
148
|
+
if (query.search) params.append("search", query.search);
|
149
|
+
if (query.sortBy) {
|
150
|
+
query.sortBy.forEach((sort) => params.append("sortBy", `${sort.key}:${sort.direction}`));
|
151
|
+
}
|
152
|
+
if (query.filter) {
|
153
|
+
Object.entries(query.filter).forEach(([field, value]) => {
|
154
|
+
params.append(`filter.${field}`, String(value));
|
155
|
+
});
|
156
|
+
}
|
157
|
+
const queryString = params.toString();
|
158
|
+
return queryString ? `?${queryString}` : "";
|
159
|
+
}
|
160
|
+
function isApiError(obj) {
|
161
|
+
return obj && typeof obj.status === "number" && typeof obj.message === "string" && obj.config === void 0;
|
162
|
+
}
|
163
|
+
function isAxiosResponse(obj) {
|
164
|
+
return obj && obj.data !== void 0 && obj.status !== void 0 && obj.config !== void 0;
|
165
|
+
}
|
166
|
+
|
246
167
|
// src/core/processor.ts
|
247
168
|
var processResponse = (responseOrError) => {
|
248
169
|
if (isApiError(responseOrError)) {
|
@@ -250,6 +171,7 @@ var processResponse = (responseOrError) => {
|
|
250
171
|
response: void 0,
|
251
172
|
error: responseOrError,
|
252
173
|
validationErrors: responseOrError.errors || [],
|
174
|
+
// الخطأ الآن صحيح
|
253
175
|
success: false,
|
254
176
|
loading: false,
|
255
177
|
message: responseOrError.message
|
@@ -257,18 +179,15 @@ var processResponse = (responseOrError) => {
|
|
257
179
|
}
|
258
180
|
if (isAxiosResponse(responseOrError)) {
|
259
181
|
const response = responseOrError;
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
message: response.data.message || "Request successful."
|
270
|
-
};
|
271
|
-
}
|
182
|
+
return {
|
183
|
+
response: response.data,
|
184
|
+
loading: false,
|
185
|
+
success: true,
|
186
|
+
error: null,
|
187
|
+
message: response.data.message || "Request successful.",
|
188
|
+
validationErrors: []
|
189
|
+
// <-- أضف قيمة افتراضية هنا للاتساق
|
190
|
+
};
|
272
191
|
}
|
273
192
|
return {
|
274
193
|
response: void 0,
|
@@ -277,7 +196,9 @@ var processResponse = (responseOrError) => {
|
|
277
196
|
status: 500
|
278
197
|
},
|
279
198
|
success: false,
|
280
|
-
loading: false
|
199
|
+
loading: false,
|
200
|
+
validationErrors: []
|
201
|
+
// <-- أضف قيمة افتراضية هنا أيضًا
|
281
202
|
};
|
282
203
|
};
|
283
204
|
|
@@ -431,9 +352,6 @@ function useApi(axiosInstance, config) {
|
|
431
352
|
// Annotate the CommonJS export names for ESM import in node:
|
432
353
|
0 && (module.exports = {
|
433
354
|
buildPaginateQuery,
|
434
|
-
cacheManager,
|
435
|
-
cancelAllRequests,
|
436
|
-
cancelRequest,
|
437
355
|
createApiClient,
|
438
356
|
createApiServices,
|
439
357
|
useApi
|
package/dist/index.mjs
CHANGED
@@ -1,198 +1,96 @@
|
|
1
1
|
// src/core/client.ts
|
2
|
-
import
|
3
|
-
import axiosRetry from "axios-retry";
|
2
|
+
import axios from "axios";
|
4
3
|
import { v4 as uuidv4 } from "uuid";
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
4
|
+
async function refreshToken(config, tokenManager) {
|
5
|
+
try {
|
6
|
+
const currentTokens = await tokenManager.getTokens();
|
7
|
+
if (!currentTokens.refreshToken) throw new Error("No refresh token available.");
|
8
|
+
const response = await axios.post(
|
9
|
+
`${config.baseURL}/auth/refresh`,
|
10
|
+
{ refresh_token: currentTokens.refreshToken },
|
11
|
+
{ withCredentials: config.withCredentials }
|
12
|
+
);
|
13
|
+
const { access_token, refresh_token, expires_in, token_type } = response.data;
|
14
|
+
const newTokens = {
|
15
|
+
accessToken: access_token,
|
16
|
+
refreshToken: refresh_token || currentTokens.refreshToken,
|
17
|
+
expiresAt: Date.now() + expires_in * 1e3,
|
18
|
+
tokenType: token_type || "Bearer"
|
19
|
+
};
|
20
|
+
await tokenManager.setTokens(newTokens);
|
21
|
+
console.log("[API Core] Tokens refreshed successfully.");
|
22
|
+
return newTokens;
|
23
|
+
} catch (err) {
|
24
|
+
console.error("[API Core] Failed to refresh token.", err);
|
25
|
+
if (config.onRefreshError) {
|
26
|
+
config.onRefreshError(err);
|
25
27
|
}
|
26
|
-
|
27
|
-
|
28
|
-
delete(key) {
|
29
|
-
this.cache.delete(key);
|
30
|
-
}
|
31
|
-
clear() {
|
32
|
-
this.cache.clear();
|
33
|
-
}
|
34
|
-
};
|
35
|
-
var cacheManager = new CacheManager();
|
36
|
-
|
37
|
-
// src/core/utils.ts
|
38
|
-
import axios from "axios";
|
39
|
-
var cancelTokens = /* @__PURE__ */ new Map();
|
40
|
-
var createCancelToken = (key) => {
|
41
|
-
const existingToken = cancelTokens.get(key);
|
42
|
-
if (existingToken) {
|
43
|
-
existingToken.cancel("Operation canceled due to new request.");
|
44
|
-
cancelTokens.delete(key);
|
45
|
-
}
|
46
|
-
const source = axios.CancelToken.source();
|
47
|
-
cancelTokens.set(key, source);
|
48
|
-
return source;
|
49
|
-
};
|
50
|
-
var cancelRequest = (key) => {
|
51
|
-
const source = cancelTokens.get(key);
|
52
|
-
if (source) {
|
53
|
-
source.cancel("Operation canceled by user.");
|
54
|
-
cancelTokens.delete(key);
|
55
|
-
}
|
56
|
-
};
|
57
|
-
var cancelAllRequests = () => {
|
58
|
-
cancelTokens.forEach((source) => source.cancel("All operations canceled."));
|
59
|
-
cancelTokens.clear();
|
60
|
-
};
|
61
|
-
function buildPaginateQuery(query) {
|
62
|
-
const params = new URLSearchParams();
|
63
|
-
if (!query) return "";
|
64
|
-
if (query.page) params.append("page", query.page.toString());
|
65
|
-
if (query.limit) params.append("limit", query.limit.toString());
|
66
|
-
if (query.search) params.append("search", query.search);
|
67
|
-
if (query.sortBy) {
|
68
|
-
query.sortBy.forEach((sort) => params.append("sortBy", `${sort.key}:${sort.direction}`));
|
69
|
-
}
|
70
|
-
if (query.filter) {
|
71
|
-
Object.entries(query.filter).forEach(([field, value]) => {
|
72
|
-
params.append(`filter.${field}`, value);
|
73
|
-
});
|
28
|
+
await tokenManager.clearTokens();
|
29
|
+
return null;
|
74
30
|
}
|
75
|
-
const queryString = params.toString();
|
76
|
-
return queryString ? `?${queryString}` : "";
|
77
|
-
}
|
78
|
-
function isApiError(obj) {
|
79
|
-
return obj && typeof obj.status === "number" && typeof obj.message === "string" && obj.config === void 0;
|
80
31
|
}
|
81
|
-
function isAxiosResponse(obj) {
|
82
|
-
return obj && obj.data !== void 0 && obj.status !== void 0 && obj.config !== void 0;
|
83
|
-
}
|
84
|
-
|
85
|
-
// src/core/client.ts
|
86
32
|
function createApiClient(config) {
|
87
33
|
const {
|
88
34
|
baseURL,
|
89
|
-
|
35
|
+
tokenManager,
|
36
|
+
timeout = 15e3,
|
90
37
|
headers = {},
|
91
|
-
|
92
|
-
getRefreshToken,
|
93
|
-
handleTokenRefresh,
|
94
|
-
onTokenRefreshSuccess,
|
95
|
-
onTokenRefreshError
|
38
|
+
withCredentials = false
|
96
39
|
} = config;
|
97
|
-
const axiosInstance =
|
40
|
+
const axiosInstance = axios.create({
|
98
41
|
baseURL,
|
99
42
|
timeout,
|
100
|
-
headers: {
|
101
|
-
|
102
|
-
Accept: "application/json",
|
103
|
-
...headers
|
104
|
-
},
|
105
|
-
withCredentials: true
|
106
|
-
});
|
107
|
-
axiosRetry(axiosInstance, {
|
108
|
-
retries: 3,
|
109
|
-
retryDelay: axiosRetry.exponentialDelay,
|
110
|
-
retryCondition: (error) => axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response?.status === 429
|
43
|
+
headers: { "Content-Type": "application/json", ...headers },
|
44
|
+
withCredentials
|
111
45
|
});
|
112
|
-
let
|
113
|
-
let requestQueue = [];
|
46
|
+
let tokenRefreshPromise = null;
|
114
47
|
axiosInstance.interceptors.request.use(async (req) => {
|
115
48
|
req.headers["X-Request-ID"] = uuidv4();
|
116
|
-
if (
|
117
|
-
|
118
|
-
|
119
|
-
req.headers.Authorization = `Bearer ${token}`;
|
120
|
-
}
|
49
|
+
if (req.url?.includes("/auth/")) return req;
|
50
|
+
if (tokenRefreshPromise) {
|
51
|
+
await tokenRefreshPromise;
|
121
52
|
}
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
if (
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
console.log(`[Cache] HIT for ${cacheKey}`);
|
130
|
-
req.adapter = () => Promise.resolve({
|
131
|
-
data: cachedData,
|
132
|
-
status: 200,
|
133
|
-
statusText: "OK (from cache)",
|
134
|
-
headers: req.headers,
|
135
|
-
config: req
|
136
|
-
});
|
137
|
-
req.cached = true;
|
53
|
+
let tokens = await tokenManager.getTokens();
|
54
|
+
const now = Date.now();
|
55
|
+
const tokenBuffer = 60 * 1e3;
|
56
|
+
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
|
57
|
+
if (!tokenRefreshPromise) {
|
58
|
+
console.log("[API Core] Proactive token refresh initiated.");
|
59
|
+
tokenRefreshPromise = refreshToken(config, tokenManager);
|
138
60
|
}
|
61
|
+
const newTokens = await tokenRefreshPromise;
|
62
|
+
tokenRefreshPromise = null;
|
63
|
+
if (newTokens) tokens = newTokens;
|
64
|
+
}
|
65
|
+
if (tokens.accessToken && !tokenManager.isHttpOnly()) {
|
66
|
+
const tokenType = tokens.tokenType || "Bearer";
|
67
|
+
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
139
68
|
}
|
140
69
|
return req;
|
141
70
|
}, (error) => Promise.reject(error));
|
142
71
|
axiosInstance.interceptors.response.use(
|
143
|
-
(response) =>
|
144
|
-
const config2 = response.config;
|
145
|
-
if (response.config.method?.toLowerCase() === "get" && config2.cache) {
|
146
|
-
const cacheKey = response.config.url || "default-cache-key";
|
147
|
-
const cacheConfig = typeof config2.cache === "object" ? config2.cache : { enabled: true };
|
148
|
-
if (cacheConfig.enabled) {
|
149
|
-
console.log(`[Cache] SET for ${cacheKey}`);
|
150
|
-
cacheManager.set(cacheKey, response.data, cacheConfig.duration);
|
151
|
-
}
|
152
|
-
}
|
153
|
-
return response;
|
154
|
-
},
|
72
|
+
(response) => response,
|
155
73
|
async (error) => {
|
156
74
|
const originalRequest = error.config;
|
157
|
-
if (error.response?.status === 401 && !originalRequest._retry
|
75
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
158
76
|
originalRequest._retry = true;
|
159
|
-
if (
|
160
|
-
|
161
|
-
|
162
|
-
originalRequest.headers.Authorization = `Bearer ${token}`;
|
163
|
-
resolve(axiosInstance(originalRequest));
|
164
|
-
});
|
165
|
-
});
|
77
|
+
if (!tokenRefreshPromise) {
|
78
|
+
console.log("[API Core] Reactive token refresh initiated due to 401.");
|
79
|
+
tokenRefreshPromise = refreshToken(config, tokenManager);
|
166
80
|
}
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
if (
|
171
|
-
|
172
|
-
if (onTokenRefreshSuccess) {
|
173
|
-
onTokenRefreshSuccess(newAccessToken, newRefreshToken);
|
81
|
+
const newTokens = await tokenRefreshPromise;
|
82
|
+
tokenRefreshPromise = null;
|
83
|
+
if (newTokens) {
|
84
|
+
if (newTokens.accessToken && !tokenManager.isHttpOnly()) {
|
85
|
+
originalRequest.headers.Authorization = `${newTokens.tokenType || "Bearer"} ${newTokens.accessToken}`;
|
174
86
|
}
|
175
|
-
axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${newAccessToken}`;
|
176
|
-
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
177
|
-
requestQueue.forEach((cb) => cb(newAccessToken));
|
178
|
-
requestQueue = [];
|
179
87
|
return axiosInstance(originalRequest);
|
180
|
-
} catch (refreshError) {
|
181
|
-
if (onTokenRefreshError) {
|
182
|
-
onTokenRefreshError(refreshError);
|
183
|
-
}
|
184
|
-
requestQueue = [];
|
185
|
-
return Promise.reject(refreshError);
|
186
|
-
} finally {
|
187
|
-
isRefreshing = false;
|
188
88
|
}
|
189
89
|
}
|
190
90
|
const enhancedError = {
|
191
|
-
message: error.response?.data?.message || error.message
|
91
|
+
message: error.response?.data?.message || error.message,
|
192
92
|
status: error.response?.status || 500,
|
193
|
-
code: error.code,
|
194
93
|
errors: error.response?.data?.errors,
|
195
|
-
path: originalRequest.url,
|
196
94
|
requestId: originalRequest.headers?.["X-Request-ID"]
|
197
95
|
};
|
198
96
|
return Promise.reject(enhancedError);
|
@@ -201,6 +99,32 @@ function createApiClient(config) {
|
|
201
99
|
return axiosInstance;
|
202
100
|
}
|
203
101
|
|
102
|
+
// src/core/utils.ts
|
103
|
+
import axios2 from "axios";
|
104
|
+
function buildPaginateQuery(query) {
|
105
|
+
if (!query) return "";
|
106
|
+
const params = new URLSearchParams();
|
107
|
+
if (query.page) params.append("page", query.page.toString());
|
108
|
+
if (query.limit) params.append("limit", query.limit.toString());
|
109
|
+
if (query.search) params.append("search", query.search);
|
110
|
+
if (query.sortBy) {
|
111
|
+
query.sortBy.forEach((sort) => params.append("sortBy", `${sort.key}:${sort.direction}`));
|
112
|
+
}
|
113
|
+
if (query.filter) {
|
114
|
+
Object.entries(query.filter).forEach(([field, value]) => {
|
115
|
+
params.append(`filter.${field}`, String(value));
|
116
|
+
});
|
117
|
+
}
|
118
|
+
const queryString = params.toString();
|
119
|
+
return queryString ? `?${queryString}` : "";
|
120
|
+
}
|
121
|
+
function isApiError(obj) {
|
122
|
+
return obj && typeof obj.status === "number" && typeof obj.message === "string" && obj.config === void 0;
|
123
|
+
}
|
124
|
+
function isAxiosResponse(obj) {
|
125
|
+
return obj && obj.data !== void 0 && obj.status !== void 0 && obj.config !== void 0;
|
126
|
+
}
|
127
|
+
|
204
128
|
// src/core/processor.ts
|
205
129
|
var processResponse = (responseOrError) => {
|
206
130
|
if (isApiError(responseOrError)) {
|
@@ -208,6 +132,7 @@ var processResponse = (responseOrError) => {
|
|
208
132
|
response: void 0,
|
209
133
|
error: responseOrError,
|
210
134
|
validationErrors: responseOrError.errors || [],
|
135
|
+
// الخطأ الآن صحيح
|
211
136
|
success: false,
|
212
137
|
loading: false,
|
213
138
|
message: responseOrError.message
|
@@ -215,18 +140,15 @@ var processResponse = (responseOrError) => {
|
|
215
140
|
}
|
216
141
|
if (isAxiosResponse(responseOrError)) {
|
217
142
|
const response = responseOrError;
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
message: response.data.message || "Request successful."
|
228
|
-
};
|
229
|
-
}
|
143
|
+
return {
|
144
|
+
response: response.data,
|
145
|
+
loading: false,
|
146
|
+
success: true,
|
147
|
+
error: null,
|
148
|
+
message: response.data.message || "Request successful.",
|
149
|
+
validationErrors: []
|
150
|
+
// <-- أضف قيمة افتراضية هنا للاتساق
|
151
|
+
};
|
230
152
|
}
|
231
153
|
return {
|
232
154
|
response: void 0,
|
@@ -235,7 +157,9 @@ var processResponse = (responseOrError) => {
|
|
235
157
|
status: 500
|
236
158
|
},
|
237
159
|
success: false,
|
238
|
-
loading: false
|
160
|
+
loading: false,
|
161
|
+
validationErrors: []
|
162
|
+
// <-- أضف قيمة افتراضية هنا أيضًا
|
239
163
|
};
|
240
164
|
};
|
241
165
|
|
@@ -388,9 +312,6 @@ function useApi(axiosInstance, config) {
|
|
388
312
|
}
|
389
313
|
export {
|
390
314
|
buildPaginateQuery,
|
391
|
-
cacheManager,
|
392
|
-
cancelAllRequests,
|
393
|
-
cancelRequest,
|
394
315
|
createApiClient,
|
395
316
|
createApiServices,
|
396
317
|
useApi
|