api-core-lib 4.4.3 → 5.5.4
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 +70 -21
- package/dist/index.d.ts +70 -21
- package/dist/index.js +184 -65
- package/dist/index.mjs +184 -65
- package/package.json +39 -39
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method } from 'axios';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -35,28 +35,49 @@ interface ApiError {
|
|
|
35
35
|
errors?: ValidationError[];
|
|
36
36
|
requestId?: string;
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
|
40
|
-
*/
|
|
41
38
|
interface Tokens {
|
|
42
39
|
accessToken: string | null;
|
|
43
40
|
refreshToken: string | null;
|
|
44
41
|
expiresAt?: number;
|
|
45
42
|
tokenType?: string;
|
|
46
43
|
}
|
|
47
|
-
/**
|
|
48
|
-
* واجهة لمدير التوكنات، تسمح بفصل منطق تخزين التوكن.
|
|
49
|
-
* يمكن للمستخدم توفير تطبيق لهذه الواجهة (e.g., LocalStorage, Cookies).
|
|
50
|
-
*/
|
|
51
44
|
interface TokenManager {
|
|
52
45
|
getTokens(): Promise<Tokens>;
|
|
53
46
|
setTokens(tokens: Tokens): Promise<void>;
|
|
54
47
|
clearTokens(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* دالة لتحديد سياق التشغيل.
|
|
50
|
+
* true إذا كانت التوكنات في كوكيز httpOnly (الوضع الآمن).
|
|
51
|
+
* false إذا كانت في مكان يمكن للعميل الوصول إليه (للتطبيقات غير Next.js).
|
|
52
|
+
*/
|
|
55
53
|
isHttpOnly(): boolean;
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
|
-
*
|
|
56
|
+
* واجهة لـ Logger مخصص. متوافقة مع `console`.
|
|
57
|
+
*/
|
|
58
|
+
interface Logger {
|
|
59
|
+
log(message?: any, ...optionalParams: any[]): void;
|
|
60
|
+
info(message?: any, ...optionalParams: any[]): void;
|
|
61
|
+
warn(message?: any, ...optionalParams: any[]): void;
|
|
62
|
+
error(message?: any, ...optionalParams: any[]): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* سياق البرمجيات الوسيطة الذي يتم تمريره لكل دالة.
|
|
59
66
|
*/
|
|
67
|
+
interface MiddlewareContext {
|
|
68
|
+
req: InternalAxiosRequestConfig;
|
|
69
|
+
res?: AxiosResponse;
|
|
70
|
+
error?: any;
|
|
71
|
+
logger: Logger;
|
|
72
|
+
custom?: Record<string, any>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* دالة برمجية وسيطة (Middleware Function).
|
|
76
|
+
*/
|
|
77
|
+
type Middleware = (context: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
|
|
78
|
+
interface RequestConfig extends AxiosRequestConfig {
|
|
79
|
+
isPublic?: boolean;
|
|
80
|
+
}
|
|
60
81
|
interface RequestConfig extends AxiosRequestConfig {
|
|
61
82
|
cancelTokenKey?: string;
|
|
62
83
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
|
@@ -113,7 +134,7 @@ interface RefreshTokenConfig {
|
|
|
113
134
|
};
|
|
114
135
|
}
|
|
115
136
|
/**
|
|
116
|
-
* الواجهة الرئيسية لتهيئة عميل الـ API.
|
|
137
|
+
* الواجهة الرئيسية لتهيئة عميل الـ API (محدثة بالكامل).
|
|
117
138
|
*/
|
|
118
139
|
interface ApiClientConfig {
|
|
119
140
|
baseURL?: string;
|
|
@@ -121,13 +142,16 @@ interface ApiClientConfig {
|
|
|
121
142
|
timeout?: number;
|
|
122
143
|
headers?: Record<string, string>;
|
|
123
144
|
withCredentials?: boolean;
|
|
124
|
-
responseType?: ResponseType;
|
|
125
|
-
/**
|
|
126
|
-
* إعدادات مخصصة لعملية تجديد التوكن.
|
|
127
|
-
* إذا لم يتم توفيرها، ستتوقف محاولات التجديد التلقائي.
|
|
128
|
-
*/
|
|
129
145
|
refreshTokenConfig?: RefreshTokenConfig;
|
|
130
146
|
onRefreshError?: (error: any) => void;
|
|
147
|
+
/**
|
|
148
|
+
* ✨ NEW: Logger مخصص. الافتراضي هو `console`.
|
|
149
|
+
*/
|
|
150
|
+
logger?: Logger;
|
|
151
|
+
/**
|
|
152
|
+
* ✨ NEW: مصفوفة من البرمجيات الوسيطة (Middleware) لتشغيلها مع كل طلب.
|
|
153
|
+
*/
|
|
154
|
+
middleware?: Middleware[];
|
|
131
155
|
}
|
|
132
156
|
/**
|
|
133
157
|
* يمثل بنية الاستجابة القياسية من الـ API.
|
|
@@ -191,6 +215,7 @@ interface ActionOptions {
|
|
|
191
215
|
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
|
192
216
|
*/
|
|
193
217
|
endpoint?: string;
|
|
218
|
+
refetch?: boolean;
|
|
194
219
|
}
|
|
195
220
|
/**
|
|
196
221
|
* واجهة لتهيئة الهوك `useApi`.
|
|
@@ -213,21 +238,41 @@ interface UseApiConfig<T> {
|
|
|
213
238
|
|
|
214
239
|
/**
|
|
215
240
|
* @file src/core/client.ts
|
|
216
|
-
* @description
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
241
|
+
* @description This is the heart of the API client library.
|
|
242
|
+
* The `createApiClient` function constructs and configures a sophisticated Axios instance.
|
|
243
|
+
* It features intelligent, security-first token management, a flexible middleware system,
|
|
244
|
+
* and customizable logging, all designed to work seamlessly in modern web frameworks like Next.js.
|
|
220
245
|
*/
|
|
221
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Creates and configures a new Axios instance with advanced features.
|
|
249
|
+
*
|
|
250
|
+
* This factory function is the central entry point of the library. It sets up
|
|
251
|
+
* request and response interceptors to handle:
|
|
252
|
+
* - Secure token management (supporting httpOnly and client-side storage).
|
|
253
|
+
* - A flexible middleware pipeline for custom logic.
|
|
254
|
+
* - Centralized logging for observability.
|
|
255
|
+
* - Automatic token refresh for client-side scenarios.
|
|
256
|
+
*
|
|
257
|
+
* @param {ApiClientConfig} config - The configuration object for the API client.
|
|
258
|
+
* @returns {AxiosInstance} A fully configured Axios instance ready for use.
|
|
259
|
+
*/
|
|
222
260
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
|
223
261
|
|
|
224
262
|
type CrudRequestConfig = RequestConfig & ActionOptions;
|
|
263
|
+
/**
|
|
264
|
+
* دالة مصنع (Factory Function) لإنشاء مجموعة خدمات API قابلة لإعادة الاستخدام لنقطة نهاية (endpoint) محددة.
|
|
265
|
+
* توفر عمليات CRUD كاملة بالإضافة إلى ميزات متقدمة مثل الحذف الجماعي ورفع الملفات.
|
|
266
|
+
*/
|
|
225
267
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
|
226
268
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
|
227
269
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
|
228
270
|
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
271
|
+
put: (id: string, data: T, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
229
272
|
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
230
273
|
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
274
|
+
bulkDelete: (ids: string[], config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
275
|
+
upload: (file: File, additionalData?: Record<string, any>, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
231
276
|
};
|
|
232
277
|
|
|
233
278
|
/**
|
|
@@ -275,6 +320,7 @@ declare function createApiActions<TActions extends Record<string, {
|
|
|
275
320
|
endpoint: string;
|
|
276
321
|
requestType: any;
|
|
277
322
|
responseType: any;
|
|
323
|
+
log?: boolean;
|
|
278
324
|
}>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
|
|
279
325
|
[K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
|
|
280
326
|
};
|
|
@@ -298,7 +344,7 @@ declare const cacheManager: CacheManager;
|
|
|
298
344
|
* @param responseOrError The raw Axios response or a pre-processed ApiError.
|
|
299
345
|
* @returns A standardized `StandardResponse` object with direct access to `.data`.
|
|
300
346
|
*/
|
|
301
|
-
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
|
|
347
|
+
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError, log?: boolean) => StandardResponse<T>;
|
|
302
348
|
|
|
303
349
|
declare function useApi<T extends {
|
|
304
350
|
id?: string | number;
|
|
@@ -308,8 +354,11 @@ declare function useApi<T extends {
|
|
|
308
354
|
actions: {
|
|
309
355
|
fetch: (options?: QueryOptions) => Promise<void>;
|
|
310
356
|
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
357
|
+
put: (id: string, item: T, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
311
358
|
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
312
359
|
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
360
|
+
bulkRemove: (ids: string[], options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
361
|
+
upload: (file: File, additionalData?: Record<string, any>, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
313
362
|
};
|
|
314
363
|
query: {
|
|
315
364
|
options: QueryOptions;
|
|
@@ -327,4 +376,4 @@ declare function useApi<T extends {
|
|
|
327
376
|
};
|
|
328
377
|
};
|
|
329
378
|
|
|
330
|
-
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
|
379
|
+
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type Logger, type Middleware, type MiddlewareContext, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method } from 'axios';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -35,28 +35,49 @@ interface ApiError {
|
|
|
35
35
|
errors?: ValidationError[];
|
|
36
36
|
requestId?: string;
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
|
40
|
-
*/
|
|
41
38
|
interface Tokens {
|
|
42
39
|
accessToken: string | null;
|
|
43
40
|
refreshToken: string | null;
|
|
44
41
|
expiresAt?: number;
|
|
45
42
|
tokenType?: string;
|
|
46
43
|
}
|
|
47
|
-
/**
|
|
48
|
-
* واجهة لمدير التوكنات، تسمح بفصل منطق تخزين التوكن.
|
|
49
|
-
* يمكن للمستخدم توفير تطبيق لهذه الواجهة (e.g., LocalStorage, Cookies).
|
|
50
|
-
*/
|
|
51
44
|
interface TokenManager {
|
|
52
45
|
getTokens(): Promise<Tokens>;
|
|
53
46
|
setTokens(tokens: Tokens): Promise<void>;
|
|
54
47
|
clearTokens(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* دالة لتحديد سياق التشغيل.
|
|
50
|
+
* true إذا كانت التوكنات في كوكيز httpOnly (الوضع الآمن).
|
|
51
|
+
* false إذا كانت في مكان يمكن للعميل الوصول إليه (للتطبيقات غير Next.js).
|
|
52
|
+
*/
|
|
55
53
|
isHttpOnly(): boolean;
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
|
-
*
|
|
56
|
+
* واجهة لـ Logger مخصص. متوافقة مع `console`.
|
|
57
|
+
*/
|
|
58
|
+
interface Logger {
|
|
59
|
+
log(message?: any, ...optionalParams: any[]): void;
|
|
60
|
+
info(message?: any, ...optionalParams: any[]): void;
|
|
61
|
+
warn(message?: any, ...optionalParams: any[]): void;
|
|
62
|
+
error(message?: any, ...optionalParams: any[]): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* سياق البرمجيات الوسيطة الذي يتم تمريره لكل دالة.
|
|
59
66
|
*/
|
|
67
|
+
interface MiddlewareContext {
|
|
68
|
+
req: InternalAxiosRequestConfig;
|
|
69
|
+
res?: AxiosResponse;
|
|
70
|
+
error?: any;
|
|
71
|
+
logger: Logger;
|
|
72
|
+
custom?: Record<string, any>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* دالة برمجية وسيطة (Middleware Function).
|
|
76
|
+
*/
|
|
77
|
+
type Middleware = (context: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
|
|
78
|
+
interface RequestConfig extends AxiosRequestConfig {
|
|
79
|
+
isPublic?: boolean;
|
|
80
|
+
}
|
|
60
81
|
interface RequestConfig extends AxiosRequestConfig {
|
|
61
82
|
cancelTokenKey?: string;
|
|
62
83
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
|
@@ -113,7 +134,7 @@ interface RefreshTokenConfig {
|
|
|
113
134
|
};
|
|
114
135
|
}
|
|
115
136
|
/**
|
|
116
|
-
* الواجهة الرئيسية لتهيئة عميل الـ API.
|
|
137
|
+
* الواجهة الرئيسية لتهيئة عميل الـ API (محدثة بالكامل).
|
|
117
138
|
*/
|
|
118
139
|
interface ApiClientConfig {
|
|
119
140
|
baseURL?: string;
|
|
@@ -121,13 +142,16 @@ interface ApiClientConfig {
|
|
|
121
142
|
timeout?: number;
|
|
122
143
|
headers?: Record<string, string>;
|
|
123
144
|
withCredentials?: boolean;
|
|
124
|
-
responseType?: ResponseType;
|
|
125
|
-
/**
|
|
126
|
-
* إعدادات مخصصة لعملية تجديد التوكن.
|
|
127
|
-
* إذا لم يتم توفيرها، ستتوقف محاولات التجديد التلقائي.
|
|
128
|
-
*/
|
|
129
145
|
refreshTokenConfig?: RefreshTokenConfig;
|
|
130
146
|
onRefreshError?: (error: any) => void;
|
|
147
|
+
/**
|
|
148
|
+
* ✨ NEW: Logger مخصص. الافتراضي هو `console`.
|
|
149
|
+
*/
|
|
150
|
+
logger?: Logger;
|
|
151
|
+
/**
|
|
152
|
+
* ✨ NEW: مصفوفة من البرمجيات الوسيطة (Middleware) لتشغيلها مع كل طلب.
|
|
153
|
+
*/
|
|
154
|
+
middleware?: Middleware[];
|
|
131
155
|
}
|
|
132
156
|
/**
|
|
133
157
|
* يمثل بنية الاستجابة القياسية من الـ API.
|
|
@@ -191,6 +215,7 @@ interface ActionOptions {
|
|
|
191
215
|
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
|
192
216
|
*/
|
|
193
217
|
endpoint?: string;
|
|
218
|
+
refetch?: boolean;
|
|
194
219
|
}
|
|
195
220
|
/**
|
|
196
221
|
* واجهة لتهيئة الهوك `useApi`.
|
|
@@ -213,21 +238,41 @@ interface UseApiConfig<T> {
|
|
|
213
238
|
|
|
214
239
|
/**
|
|
215
240
|
* @file src/core/client.ts
|
|
216
|
-
* @description
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
241
|
+
* @description This is the heart of the API client library.
|
|
242
|
+
* The `createApiClient` function constructs and configures a sophisticated Axios instance.
|
|
243
|
+
* It features intelligent, security-first token management, a flexible middleware system,
|
|
244
|
+
* and customizable logging, all designed to work seamlessly in modern web frameworks like Next.js.
|
|
220
245
|
*/
|
|
221
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Creates and configures a new Axios instance with advanced features.
|
|
249
|
+
*
|
|
250
|
+
* This factory function is the central entry point of the library. It sets up
|
|
251
|
+
* request and response interceptors to handle:
|
|
252
|
+
* - Secure token management (supporting httpOnly and client-side storage).
|
|
253
|
+
* - A flexible middleware pipeline for custom logic.
|
|
254
|
+
* - Centralized logging for observability.
|
|
255
|
+
* - Automatic token refresh for client-side scenarios.
|
|
256
|
+
*
|
|
257
|
+
* @param {ApiClientConfig} config - The configuration object for the API client.
|
|
258
|
+
* @returns {AxiosInstance} A fully configured Axios instance ready for use.
|
|
259
|
+
*/
|
|
222
260
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
|
223
261
|
|
|
224
262
|
type CrudRequestConfig = RequestConfig & ActionOptions;
|
|
263
|
+
/**
|
|
264
|
+
* دالة مصنع (Factory Function) لإنشاء مجموعة خدمات API قابلة لإعادة الاستخدام لنقطة نهاية (endpoint) محددة.
|
|
265
|
+
* توفر عمليات CRUD كاملة بالإضافة إلى ميزات متقدمة مثل الحذف الجماعي ورفع الملفات.
|
|
266
|
+
*/
|
|
225
267
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
|
226
268
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
|
227
269
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
|
228
270
|
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
271
|
+
put: (id: string, data: T, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
229
272
|
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
|
230
273
|
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
274
|
+
bulkDelete: (ids: string[], config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
275
|
+
upload: (file: File, additionalData?: Record<string, any>, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
|
231
276
|
};
|
|
232
277
|
|
|
233
278
|
/**
|
|
@@ -275,6 +320,7 @@ declare function createApiActions<TActions extends Record<string, {
|
|
|
275
320
|
endpoint: string;
|
|
276
321
|
requestType: any;
|
|
277
322
|
responseType: any;
|
|
323
|
+
log?: boolean;
|
|
278
324
|
}>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
|
|
279
325
|
[K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
|
|
280
326
|
};
|
|
@@ -298,7 +344,7 @@ declare const cacheManager: CacheManager;
|
|
|
298
344
|
* @param responseOrError The raw Axios response or a pre-processed ApiError.
|
|
299
345
|
* @returns A standardized `StandardResponse` object with direct access to `.data`.
|
|
300
346
|
*/
|
|
301
|
-
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
|
|
347
|
+
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError, log?: boolean) => StandardResponse<T>;
|
|
302
348
|
|
|
303
349
|
declare function useApi<T extends {
|
|
304
350
|
id?: string | number;
|
|
@@ -308,8 +354,11 @@ declare function useApi<T extends {
|
|
|
308
354
|
actions: {
|
|
309
355
|
fetch: (options?: QueryOptions) => Promise<void>;
|
|
310
356
|
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
357
|
+
put: (id: string, item: T, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
311
358
|
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
|
312
359
|
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
360
|
+
bulkRemove: (ids: string[], options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
361
|
+
upload: (file: File, additionalData?: Record<string, any>, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
|
313
362
|
};
|
|
314
363
|
query: {
|
|
315
364
|
options: QueryOptions;
|
|
@@ -327,4 +376,4 @@ declare function useApi<T extends {
|
|
|
327
376
|
};
|
|
328
377
|
};
|
|
329
378
|
|
|
330
|
-
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
|
379
|
+
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type Logger, type Middleware, type MiddlewareContext, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
package/dist/index.js
CHANGED
|
@@ -43,10 +43,20 @@ module.exports = __toCommonJS(index_exports);
|
|
|
43
43
|
// src/core/client.ts
|
|
44
44
|
var import_axios = __toESM(require("axios"));
|
|
45
45
|
var import_uuid = require("uuid");
|
|
46
|
-
async function
|
|
46
|
+
async function runMiddleware(context, middlewares = []) {
|
|
47
|
+
const run = async (index) => {
|
|
48
|
+
if (index >= middlewares.length) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const middleware = middlewares[index];
|
|
52
|
+
await middleware(context, () => run(index + 1));
|
|
53
|
+
};
|
|
54
|
+
await run(0);
|
|
55
|
+
}
|
|
56
|
+
async function refreshToken(config, tokenManager, logger) {
|
|
47
57
|
const { refreshTokenConfig } = config;
|
|
48
58
|
if (!refreshTokenConfig) {
|
|
49
|
-
|
|
59
|
+
logger.warn("[API Core] Token refresh is disabled: `refreshTokenConfig` is not provided.");
|
|
50
60
|
await tokenManager.clearTokens();
|
|
51
61
|
return null;
|
|
52
62
|
}
|
|
@@ -58,6 +68,7 @@ async function refreshToken(config, tokenManager) {
|
|
|
58
68
|
const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;
|
|
59
69
|
const requestBody = buildRequestBody ? buildRequestBody(currentTokens.refreshToken) : { refresh_token: currentTokens.refreshToken };
|
|
60
70
|
const requestHeaders = buildRequestHeaders ? buildRequestHeaders(currentTokens) : {};
|
|
71
|
+
logger.info(`[API Core] Attempting to refresh token at path: ${path}`);
|
|
61
72
|
const response = await import_axios.default.post(
|
|
62
73
|
`${config.baseURL}${path}`,
|
|
63
74
|
requestBody,
|
|
@@ -73,15 +84,14 @@ async function refreshToken(config, tokenManager) {
|
|
|
73
84
|
const newTokens = {
|
|
74
85
|
accessToken: extracted.accessToken,
|
|
75
86
|
refreshToken: extracted.refreshToken || currentTokens.refreshToken,
|
|
76
|
-
// احتفظ بالقديم إذا لم يأتِ جديد
|
|
77
87
|
expiresAt: Date.now() + extracted.expiresIn * 1e3,
|
|
78
88
|
tokenType: extracted.tokenType || "Bearer"
|
|
79
89
|
};
|
|
80
90
|
await tokenManager.setTokens(newTokens);
|
|
81
|
-
|
|
91
|
+
logger.info("[API Core] Tokens refreshed successfully.");
|
|
82
92
|
return newTokens;
|
|
83
93
|
} catch (err) {
|
|
84
|
-
|
|
94
|
+
logger.error("[API Core] Failed to refresh token.", err.response?.data || err.message);
|
|
85
95
|
if (config.onRefreshError) {
|
|
86
96
|
config.onRefreshError(err);
|
|
87
97
|
}
|
|
@@ -95,10 +105,9 @@ function createApiClient(config) {
|
|
|
95
105
|
tokenManager,
|
|
96
106
|
timeout = 15e3,
|
|
97
107
|
headers = {},
|
|
98
|
-
withCredentials = false
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// refreshTokenConfig,
|
|
108
|
+
withCredentials = false,
|
|
109
|
+
middleware = [],
|
|
110
|
+
logger = console
|
|
102
111
|
} = config;
|
|
103
112
|
const axiosInstance = import_axios.default.create({
|
|
104
113
|
baseURL,
|
|
@@ -106,63 +115,88 @@ function createApiClient(config) {
|
|
|
106
115
|
headers: { "Content-Type": "application/json", ...headers },
|
|
107
116
|
withCredentials
|
|
108
117
|
});
|
|
109
|
-
let
|
|
118
|
+
let isRefreshing = false;
|
|
119
|
+
let failedQueue = [];
|
|
120
|
+
const processQueue = (error, token = null) => {
|
|
121
|
+
failedQueue.forEach((prom) => error ? prom.reject(error) : prom.resolve(token));
|
|
122
|
+
failedQueue = [];
|
|
123
|
+
};
|
|
110
124
|
axiosInstance.interceptors.request.use(async (req) => {
|
|
111
125
|
req.headers["X-Request-ID"] = (0, import_uuid.v4)();
|
|
126
|
+
logger.info(`[Request] > ${req.method?.toUpperCase()} ${req.url}`, { id: req.headers["X-Request-ID"] });
|
|
127
|
+
const context = { req, logger, custom: {} };
|
|
128
|
+
await runMiddleware(context, middleware);
|
|
112
129
|
if (req.isPublic) {
|
|
113
|
-
console.log(`[API Core] Skipping token for public request: ${req.url}`);
|
|
114
130
|
return req;
|
|
115
131
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
132
|
+
if (tokenManager.isHttpOnly()) {
|
|
133
|
+
return req;
|
|
118
134
|
}
|
|
119
135
|
let tokens = await tokenManager.getTokens();
|
|
120
136
|
const now = Date.now();
|
|
121
137
|
const tokenBuffer = 60 * 1e3;
|
|
122
|
-
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
138
|
+
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer && !isRefreshing) {
|
|
139
|
+
if (config.refreshTokenConfig) {
|
|
140
|
+
logger.info("[API Core] Proactive token refresh initiated.");
|
|
141
|
+
isRefreshing = true;
|
|
142
|
+
try {
|
|
143
|
+
const newTokens = await refreshToken(config, tokenManager, logger);
|
|
144
|
+
if (newTokens) tokens = newTokens;
|
|
145
|
+
} finally {
|
|
146
|
+
isRefreshing = false;
|
|
147
|
+
}
|
|
126
148
|
}
|
|
127
|
-
const newTokens = await tokenRefreshPromise;
|
|
128
|
-
tokenRefreshPromise = null;
|
|
129
|
-
if (newTokens) tokens = newTokens;
|
|
130
149
|
}
|
|
131
|
-
if (tokens.accessToken
|
|
132
|
-
|
|
133
|
-
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
|
134
|
-
console.log(`[API Core] Token attached to request: ${req.url}`);
|
|
135
|
-
} else {
|
|
136
|
-
console.warn(`[API Core] No token attached for request: ${req.url}`);
|
|
150
|
+
if (tokens.accessToken) {
|
|
151
|
+
req.headers.Authorization = `${tokens.tokenType || "Bearer"} ${tokens.accessToken}`;
|
|
137
152
|
}
|
|
138
153
|
return req;
|
|
139
|
-
}
|
|
154
|
+
});
|
|
140
155
|
axiosInstance.interceptors.response.use(
|
|
141
|
-
(
|
|
156
|
+
async (res) => {
|
|
157
|
+
logger.info(`[Response] < ${res.config.method?.toUpperCase()} ${res.config.url}`, { id: res.config.headers["X-Request-ID"], status: res.status });
|
|
158
|
+
const context = { req: res.config, res, logger, custom: {} };
|
|
159
|
+
await runMiddleware(context, middleware);
|
|
160
|
+
return res;
|
|
161
|
+
},
|
|
142
162
|
async (error) => {
|
|
143
163
|
const originalRequest = error.config;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
logger.error(`[Response Error] < ${originalRequest.method?.toUpperCase()} ${originalRequest.url}`, { id: originalRequest.headers?.["X-Request-ID"], status: error.response?.status, error: error.message });
|
|
165
|
+
const context = { req: originalRequest, error, logger, custom: {} };
|
|
166
|
+
await runMiddleware(context, middleware);
|
|
167
|
+
const isHttpOnlyMode = tokenManager.isHttpOnly();
|
|
168
|
+
const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;
|
|
169
|
+
if (isHttpOnlyMode && canAttemptRefresh) {
|
|
170
|
+
logger.error("[API Core] Fatal: Received 401 in httpOnly mode. Token refresh must be handled by the server-side proxy. The request will not be retried from the client.");
|
|
171
|
+
return Promise.reject(error);
|
|
172
|
+
}
|
|
173
|
+
if (!isHttpOnlyMode && canAttemptRefresh) {
|
|
174
|
+
if (isRefreshing) {
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
failedQueue.push({ resolve, reject });
|
|
177
|
+
}).then((token) => {
|
|
178
|
+
originalRequest.headers["Authorization"] = `Bearer ${token}`;
|
|
179
|
+
return axiosInstance(originalRequest);
|
|
180
|
+
});
|
|
149
181
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
182
|
+
originalRequest._retry = true;
|
|
183
|
+
isRefreshing = true;
|
|
184
|
+
try {
|
|
185
|
+
const newTokens = await refreshToken(config, tokenManager, logger);
|
|
186
|
+
if (!newTokens || !newTokens.accessToken) {
|
|
187
|
+
throw new Error("Token refresh failed to produce a new access token.");
|
|
155
188
|
}
|
|
189
|
+
processQueue(null, newTokens.accessToken);
|
|
190
|
+
originalRequest.headers["Authorization"] = `${newTokens.tokenType || "Bearer"} ${newTokens.accessToken}`;
|
|
156
191
|
return axiosInstance(originalRequest);
|
|
192
|
+
} catch (refreshError) {
|
|
193
|
+
processQueue(refreshError, null);
|
|
194
|
+
return Promise.reject(refreshError);
|
|
195
|
+
} finally {
|
|
196
|
+
isRefreshing = false;
|
|
157
197
|
}
|
|
158
198
|
}
|
|
159
|
-
|
|
160
|
-
message: error.response?.data?.message || error.message,
|
|
161
|
-
status: error.response?.status || 500,
|
|
162
|
-
errors: error.response?.data?.errors,
|
|
163
|
-
requestId: originalRequest.headers?.["X-Request-ID"]
|
|
164
|
-
};
|
|
165
|
-
return Promise.reject(enhancedError);
|
|
199
|
+
return Promise.reject(error);
|
|
166
200
|
}
|
|
167
201
|
);
|
|
168
202
|
return axiosInstance;
|
|
@@ -204,8 +238,10 @@ function isAxiosResponse(obj) {
|
|
|
204
238
|
}
|
|
205
239
|
|
|
206
240
|
// src/core/processor.ts
|
|
207
|
-
var processResponse = (responseOrError) => {
|
|
241
|
+
var processResponse = (responseOrError, log) => {
|
|
208
242
|
if (isApiError(responseOrError)) {
|
|
243
|
+
log && console.log(`****[ API ERROR PROCESS RESPONSE ]: ***
|
|
244
|
+
${responseOrError} *** `);
|
|
209
245
|
return {
|
|
210
246
|
data: null,
|
|
211
247
|
rawResponse: void 0,
|
|
@@ -217,15 +253,17 @@ var processResponse = (responseOrError) => {
|
|
|
217
253
|
};
|
|
218
254
|
}
|
|
219
255
|
if (isAxiosResponse(responseOrError)) {
|
|
256
|
+
log && console.log(`****[ API IS SCSS PROCESS RESPONSE ]: ***
|
|
257
|
+
${responseOrError} *** `);
|
|
220
258
|
const rawData = responseOrError.data;
|
|
221
259
|
const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
|
|
260
|
+
log && console.log(`12345[ API IS WRAPPED DATA PROCESS RESPONSE ]: ***
|
|
261
|
+
${isWrappedResponse} *** `);
|
|
222
262
|
const finalData = isWrappedResponse ? rawData.data : rawData;
|
|
223
263
|
const message = isWrappedResponse ? rawData.message : "Request successful.";
|
|
224
264
|
return {
|
|
225
265
|
data: finalData,
|
|
226
|
-
// <-- وصول مباشر للبيانات النهائية!
|
|
227
266
|
rawResponse: rawData,
|
|
228
|
-
// احتفظ بالاستجابة الكاملة إذا احتجت إليها
|
|
229
267
|
loading: false,
|
|
230
268
|
success: true,
|
|
231
269
|
error: null,
|
|
@@ -233,6 +271,7 @@ var processResponse = (responseOrError) => {
|
|
|
233
271
|
validationErrors: []
|
|
234
272
|
};
|
|
235
273
|
}
|
|
274
|
+
log && console.log(`****[ API STATUS 500 ]: *** ${responseOrError} *** `);
|
|
236
275
|
return {
|
|
237
276
|
data: null,
|
|
238
277
|
rawResponse: void 0,
|
|
@@ -270,6 +309,15 @@ function createApiServices(axiosInstance, endpoint) {
|
|
|
270
309
|
return processResponse(error);
|
|
271
310
|
}
|
|
272
311
|
};
|
|
312
|
+
const put = async (id, data, config) => {
|
|
313
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
|
314
|
+
try {
|
|
315
|
+
const response = await axiosInstance.put(finalUrl, data, config);
|
|
316
|
+
return processResponse(response);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
return processResponse(error);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
273
321
|
const patch = async (id, data, config) => {
|
|
274
322
|
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
|
275
323
|
try {
|
|
@@ -288,23 +336,57 @@ function createApiServices(axiosInstance, endpoint) {
|
|
|
288
336
|
return processResponse(error);
|
|
289
337
|
}
|
|
290
338
|
};
|
|
291
|
-
|
|
339
|
+
const bulkDelete = async (ids, config) => {
|
|
340
|
+
const finalUrl = config?.endpoint || `${endpoint}`;
|
|
341
|
+
try {
|
|
342
|
+
const response = await axiosInstance.delete(finalUrl, { data: { ids }, ...config });
|
|
343
|
+
return processResponse(response);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
return processResponse(error);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const upload = async (file, additionalData, config) => {
|
|
349
|
+
const finalUrl = config?.endpoint || `${endpoint}`;
|
|
350
|
+
const formData = new FormData();
|
|
351
|
+
formData.append("file", file);
|
|
352
|
+
if (additionalData) {
|
|
353
|
+
Object.keys(additionalData).forEach((key) => {
|
|
354
|
+
formData.append(key, additionalData[key]);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
const response = await axiosInstance.post(finalUrl, formData, {
|
|
359
|
+
...config,
|
|
360
|
+
headers: {
|
|
361
|
+
...config?.headers,
|
|
362
|
+
"Content-Type": "multipart/form-data"
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
return processResponse(response);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
return processResponse(error);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };
|
|
292
371
|
}
|
|
293
372
|
|
|
294
373
|
// src/services/actions.ts
|
|
295
|
-
function createAction(axiosInstance, method, endpoint) {
|
|
374
|
+
function createAction(axiosInstance, method, endpoint, log) {
|
|
296
375
|
return async (payload, config) => {
|
|
376
|
+
log && console.log(`*[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} **** [${payload}]** `);
|
|
297
377
|
try {
|
|
378
|
+
log && console.log(`**[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]:${endpoint} **** [action try req]** `);
|
|
298
379
|
const response = await axiosInstance.request({
|
|
299
|
-
// <--- غيرنا النوع إلى any
|
|
300
380
|
url: endpoint,
|
|
301
381
|
method,
|
|
302
382
|
...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
|
|
303
383
|
...config
|
|
304
384
|
});
|
|
305
|
-
|
|
385
|
+
log && console.log(`***[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} ***
|
|
386
|
+
${response}*** `);
|
|
387
|
+
return processResponse(response, log);
|
|
306
388
|
} catch (error) {
|
|
307
|
-
return processResponse(error);
|
|
389
|
+
return processResponse(error, log);
|
|
308
390
|
}
|
|
309
391
|
};
|
|
310
392
|
}
|
|
@@ -312,8 +394,8 @@ function createApiActions(axiosInstance, actionsConfig) {
|
|
|
312
394
|
const actions = {};
|
|
313
395
|
for (const actionName in actionsConfig) {
|
|
314
396
|
if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
|
|
315
|
-
const { method, endpoint } = actionsConfig[actionName];
|
|
316
|
-
actions[actionName] = createAction(axiosInstance, method, endpoint);
|
|
397
|
+
const { method, endpoint, log } = actionsConfig[actionName];
|
|
398
|
+
actions[actionName] = createAction(axiosInstance, method, endpoint, log);
|
|
317
399
|
}
|
|
318
400
|
}
|
|
319
401
|
return actions;
|
|
@@ -379,7 +461,6 @@ function useApi(axiosInstance, config) {
|
|
|
379
461
|
const result = await apiServices.getWithQuery(queryString, {
|
|
380
462
|
cancelTokenKey: endpoint,
|
|
381
463
|
...requestConfig
|
|
382
|
-
// <-- هنا نمرر الإعدادات الافتراضية
|
|
383
464
|
});
|
|
384
465
|
setState(result);
|
|
385
466
|
if (!result.success && onError) {
|
|
@@ -404,6 +485,19 @@ function useApi(axiosInstance, config) {
|
|
|
404
485
|
}
|
|
405
486
|
return result;
|
|
406
487
|
};
|
|
488
|
+
const putItem = async (id, item, options) => {
|
|
489
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
490
|
+
const result = await apiServices.put(id, item, options);
|
|
491
|
+
if (result.success) {
|
|
492
|
+
if (refetchAfterChange) await fetchData();
|
|
493
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
494
|
+
if (onSuccess) onSuccess(result.message || "Item replaced successfully!", result.data);
|
|
495
|
+
} else {
|
|
496
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
497
|
+
if (onError) onError(result.message || "Replace failed", result.error || void 0);
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
};
|
|
407
501
|
const updateItem = async (id, updatedItem, options) => {
|
|
408
502
|
setState((prev) => ({ ...prev, loading: true }));
|
|
409
503
|
const result = await apiServices.patch(id, updatedItem, options);
|
|
@@ -430,19 +524,39 @@ function useApi(axiosInstance, config) {
|
|
|
430
524
|
}
|
|
431
525
|
return result;
|
|
432
526
|
};
|
|
527
|
+
const bulkDeleteItem = async (ids, options) => {
|
|
528
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
529
|
+
const result = await apiServices.bulkDelete(ids, options);
|
|
530
|
+
if (result.success) {
|
|
531
|
+
if (refetchAfterChange) await fetchData();
|
|
532
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
533
|
+
if (onSuccess) onSuccess(result.message || "Items deleted successfully!");
|
|
534
|
+
} else {
|
|
535
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
536
|
+
if (onError) onError(result.message || "Bulk delete failed", result.error || void 0);
|
|
537
|
+
}
|
|
538
|
+
return result;
|
|
539
|
+
};
|
|
540
|
+
const uploadFile = async (file, additionalData, options) => {
|
|
541
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
542
|
+
const result = await apiServices.upload(file, additionalData, options);
|
|
543
|
+
if (result.success) {
|
|
544
|
+
if (refetchAfterChange && options?.refetch !== false) await fetchData();
|
|
545
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
546
|
+
if (onSuccess) onSuccess(result.message || "File uploaded successfully!", result.data);
|
|
547
|
+
} else {
|
|
548
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
549
|
+
if (onError) onError(result.message || "Upload failed", result.error || void 0);
|
|
550
|
+
}
|
|
551
|
+
return result;
|
|
552
|
+
};
|
|
433
553
|
const setPage = (page) => setQueryOptions((prev) => ({ ...prev, page }));
|
|
434
554
|
const setLimit = (limit) => setQueryOptions((prev) => ({ ...prev, limit, page: 1 }));
|
|
435
555
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
|
436
556
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
|
437
557
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
|
438
558
|
const setQueryParam = (key, value) => {
|
|
439
|
-
setQueryOptions((prev) => {
|
|
440
|
-
const newQuery = { ...prev, [key]: value };
|
|
441
|
-
if (key !== "page") {
|
|
442
|
-
newQuery.page = 1;
|
|
443
|
-
}
|
|
444
|
-
return newQuery;
|
|
445
|
-
});
|
|
559
|
+
setQueryOptions((prev) => ({ ...prev, [key]: value, page: key !== "page" ? 1 : prev.page }));
|
|
446
560
|
};
|
|
447
561
|
const resetQuery = () => setQueryOptions(initialQuery);
|
|
448
562
|
return {
|
|
@@ -451,8 +565,14 @@ function useApi(axiosInstance, config) {
|
|
|
451
565
|
actions: {
|
|
452
566
|
fetch: fetchData,
|
|
453
567
|
create: createItem,
|
|
568
|
+
put: putItem,
|
|
569
|
+
// <-- NEW
|
|
454
570
|
update: updateItem,
|
|
455
|
-
remove: deleteItem
|
|
571
|
+
remove: deleteItem,
|
|
572
|
+
bulkRemove: bulkDeleteItem,
|
|
573
|
+
// <-- NEW
|
|
574
|
+
upload: uploadFile
|
|
575
|
+
// <-- NEW
|
|
456
576
|
},
|
|
457
577
|
query: {
|
|
458
578
|
options: queryOptions,
|
|
@@ -463,7 +583,6 @@ function useApi(axiosInstance, config) {
|
|
|
463
583
|
setSorting,
|
|
464
584
|
setFilters,
|
|
465
585
|
setQueryParam,
|
|
466
|
-
// <-- NEW
|
|
467
586
|
reset: resetQuery
|
|
468
587
|
}
|
|
469
588
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
// src/core/client.ts
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
|
-
async function
|
|
4
|
+
async function runMiddleware(context, middlewares = []) {
|
|
5
|
+
const run = async (index) => {
|
|
6
|
+
if (index >= middlewares.length) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const middleware = middlewares[index];
|
|
10
|
+
await middleware(context, () => run(index + 1));
|
|
11
|
+
};
|
|
12
|
+
await run(0);
|
|
13
|
+
}
|
|
14
|
+
async function refreshToken(config, tokenManager, logger) {
|
|
5
15
|
const { refreshTokenConfig } = config;
|
|
6
16
|
if (!refreshTokenConfig) {
|
|
7
|
-
|
|
17
|
+
logger.warn("[API Core] Token refresh is disabled: `refreshTokenConfig` is not provided.");
|
|
8
18
|
await tokenManager.clearTokens();
|
|
9
19
|
return null;
|
|
10
20
|
}
|
|
@@ -16,6 +26,7 @@ async function refreshToken(config, tokenManager) {
|
|
|
16
26
|
const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;
|
|
17
27
|
const requestBody = buildRequestBody ? buildRequestBody(currentTokens.refreshToken) : { refresh_token: currentTokens.refreshToken };
|
|
18
28
|
const requestHeaders = buildRequestHeaders ? buildRequestHeaders(currentTokens) : {};
|
|
29
|
+
logger.info(`[API Core] Attempting to refresh token at path: ${path}`);
|
|
19
30
|
const response = await axios.post(
|
|
20
31
|
`${config.baseURL}${path}`,
|
|
21
32
|
requestBody,
|
|
@@ -31,15 +42,14 @@ async function refreshToken(config, tokenManager) {
|
|
|
31
42
|
const newTokens = {
|
|
32
43
|
accessToken: extracted.accessToken,
|
|
33
44
|
refreshToken: extracted.refreshToken || currentTokens.refreshToken,
|
|
34
|
-
// احتفظ بالقديم إذا لم يأتِ جديد
|
|
35
45
|
expiresAt: Date.now() + extracted.expiresIn * 1e3,
|
|
36
46
|
tokenType: extracted.tokenType || "Bearer"
|
|
37
47
|
};
|
|
38
48
|
await tokenManager.setTokens(newTokens);
|
|
39
|
-
|
|
49
|
+
logger.info("[API Core] Tokens refreshed successfully.");
|
|
40
50
|
return newTokens;
|
|
41
51
|
} catch (err) {
|
|
42
|
-
|
|
52
|
+
logger.error("[API Core] Failed to refresh token.", err.response?.data || err.message);
|
|
43
53
|
if (config.onRefreshError) {
|
|
44
54
|
config.onRefreshError(err);
|
|
45
55
|
}
|
|
@@ -53,10 +63,9 @@ function createApiClient(config) {
|
|
|
53
63
|
tokenManager,
|
|
54
64
|
timeout = 15e3,
|
|
55
65
|
headers = {},
|
|
56
|
-
withCredentials = false
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// refreshTokenConfig,
|
|
66
|
+
withCredentials = false,
|
|
67
|
+
middleware = [],
|
|
68
|
+
logger = console
|
|
60
69
|
} = config;
|
|
61
70
|
const axiosInstance = axios.create({
|
|
62
71
|
baseURL,
|
|
@@ -64,63 +73,88 @@ function createApiClient(config) {
|
|
|
64
73
|
headers: { "Content-Type": "application/json", ...headers },
|
|
65
74
|
withCredentials
|
|
66
75
|
});
|
|
67
|
-
let
|
|
76
|
+
let isRefreshing = false;
|
|
77
|
+
let failedQueue = [];
|
|
78
|
+
const processQueue = (error, token = null) => {
|
|
79
|
+
failedQueue.forEach((prom) => error ? prom.reject(error) : prom.resolve(token));
|
|
80
|
+
failedQueue = [];
|
|
81
|
+
};
|
|
68
82
|
axiosInstance.interceptors.request.use(async (req) => {
|
|
69
83
|
req.headers["X-Request-ID"] = uuidv4();
|
|
84
|
+
logger.info(`[Request] > ${req.method?.toUpperCase()} ${req.url}`, { id: req.headers["X-Request-ID"] });
|
|
85
|
+
const context = { req, logger, custom: {} };
|
|
86
|
+
await runMiddleware(context, middleware);
|
|
70
87
|
if (req.isPublic) {
|
|
71
|
-
console.log(`[API Core] Skipping token for public request: ${req.url}`);
|
|
72
88
|
return req;
|
|
73
89
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
90
|
+
if (tokenManager.isHttpOnly()) {
|
|
91
|
+
return req;
|
|
76
92
|
}
|
|
77
93
|
let tokens = await tokenManager.getTokens();
|
|
78
94
|
const now = Date.now();
|
|
79
95
|
const tokenBuffer = 60 * 1e3;
|
|
80
|
-
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer && !isRefreshing) {
|
|
97
|
+
if (config.refreshTokenConfig) {
|
|
98
|
+
logger.info("[API Core] Proactive token refresh initiated.");
|
|
99
|
+
isRefreshing = true;
|
|
100
|
+
try {
|
|
101
|
+
const newTokens = await refreshToken(config, tokenManager, logger);
|
|
102
|
+
if (newTokens) tokens = newTokens;
|
|
103
|
+
} finally {
|
|
104
|
+
isRefreshing = false;
|
|
105
|
+
}
|
|
84
106
|
}
|
|
85
|
-
const newTokens = await tokenRefreshPromise;
|
|
86
|
-
tokenRefreshPromise = null;
|
|
87
|
-
if (newTokens) tokens = newTokens;
|
|
88
107
|
}
|
|
89
|
-
if (tokens.accessToken
|
|
90
|
-
|
|
91
|
-
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
|
92
|
-
console.log(`[API Core] Token attached to request: ${req.url}`);
|
|
93
|
-
} else {
|
|
94
|
-
console.warn(`[API Core] No token attached for request: ${req.url}`);
|
|
108
|
+
if (tokens.accessToken) {
|
|
109
|
+
req.headers.Authorization = `${tokens.tokenType || "Bearer"} ${tokens.accessToken}`;
|
|
95
110
|
}
|
|
96
111
|
return req;
|
|
97
|
-
}
|
|
112
|
+
});
|
|
98
113
|
axiosInstance.interceptors.response.use(
|
|
99
|
-
(
|
|
114
|
+
async (res) => {
|
|
115
|
+
logger.info(`[Response] < ${res.config.method?.toUpperCase()} ${res.config.url}`, { id: res.config.headers["X-Request-ID"], status: res.status });
|
|
116
|
+
const context = { req: res.config, res, logger, custom: {} };
|
|
117
|
+
await runMiddleware(context, middleware);
|
|
118
|
+
return res;
|
|
119
|
+
},
|
|
100
120
|
async (error) => {
|
|
101
121
|
const originalRequest = error.config;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
logger.error(`[Response Error] < ${originalRequest.method?.toUpperCase()} ${originalRequest.url}`, { id: originalRequest.headers?.["X-Request-ID"], status: error.response?.status, error: error.message });
|
|
123
|
+
const context = { req: originalRequest, error, logger, custom: {} };
|
|
124
|
+
await runMiddleware(context, middleware);
|
|
125
|
+
const isHttpOnlyMode = tokenManager.isHttpOnly();
|
|
126
|
+
const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;
|
|
127
|
+
if (isHttpOnlyMode && canAttemptRefresh) {
|
|
128
|
+
logger.error("[API Core] Fatal: Received 401 in httpOnly mode. Token refresh must be handled by the server-side proxy. The request will not be retried from the client.");
|
|
129
|
+
return Promise.reject(error);
|
|
130
|
+
}
|
|
131
|
+
if (!isHttpOnlyMode && canAttemptRefresh) {
|
|
132
|
+
if (isRefreshing) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
failedQueue.push({ resolve, reject });
|
|
135
|
+
}).then((token) => {
|
|
136
|
+
originalRequest.headers["Authorization"] = `Bearer ${token}`;
|
|
137
|
+
return axiosInstance(originalRequest);
|
|
138
|
+
});
|
|
107
139
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
140
|
+
originalRequest._retry = true;
|
|
141
|
+
isRefreshing = true;
|
|
142
|
+
try {
|
|
143
|
+
const newTokens = await refreshToken(config, tokenManager, logger);
|
|
144
|
+
if (!newTokens || !newTokens.accessToken) {
|
|
145
|
+
throw new Error("Token refresh failed to produce a new access token.");
|
|
113
146
|
}
|
|
147
|
+
processQueue(null, newTokens.accessToken);
|
|
148
|
+
originalRequest.headers["Authorization"] = `${newTokens.tokenType || "Bearer"} ${newTokens.accessToken}`;
|
|
114
149
|
return axiosInstance(originalRequest);
|
|
150
|
+
} catch (refreshError) {
|
|
151
|
+
processQueue(refreshError, null);
|
|
152
|
+
return Promise.reject(refreshError);
|
|
153
|
+
} finally {
|
|
154
|
+
isRefreshing = false;
|
|
115
155
|
}
|
|
116
156
|
}
|
|
117
|
-
|
|
118
|
-
message: error.response?.data?.message || error.message,
|
|
119
|
-
status: error.response?.status || 500,
|
|
120
|
-
errors: error.response?.data?.errors,
|
|
121
|
-
requestId: originalRequest.headers?.["X-Request-ID"]
|
|
122
|
-
};
|
|
123
|
-
return Promise.reject(enhancedError);
|
|
157
|
+
return Promise.reject(error);
|
|
124
158
|
}
|
|
125
159
|
);
|
|
126
160
|
return axiosInstance;
|
|
@@ -162,8 +196,10 @@ function isAxiosResponse(obj) {
|
|
|
162
196
|
}
|
|
163
197
|
|
|
164
198
|
// src/core/processor.ts
|
|
165
|
-
var processResponse = (responseOrError) => {
|
|
199
|
+
var processResponse = (responseOrError, log) => {
|
|
166
200
|
if (isApiError(responseOrError)) {
|
|
201
|
+
log && console.log(`****[ API ERROR PROCESS RESPONSE ]: ***
|
|
202
|
+
${responseOrError} *** `);
|
|
167
203
|
return {
|
|
168
204
|
data: null,
|
|
169
205
|
rawResponse: void 0,
|
|
@@ -175,15 +211,17 @@ var processResponse = (responseOrError) => {
|
|
|
175
211
|
};
|
|
176
212
|
}
|
|
177
213
|
if (isAxiosResponse(responseOrError)) {
|
|
214
|
+
log && console.log(`****[ API IS SCSS PROCESS RESPONSE ]: ***
|
|
215
|
+
${responseOrError} *** `);
|
|
178
216
|
const rawData = responseOrError.data;
|
|
179
217
|
const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
|
|
218
|
+
log && console.log(`12345[ API IS WRAPPED DATA PROCESS RESPONSE ]: ***
|
|
219
|
+
${isWrappedResponse} *** `);
|
|
180
220
|
const finalData = isWrappedResponse ? rawData.data : rawData;
|
|
181
221
|
const message = isWrappedResponse ? rawData.message : "Request successful.";
|
|
182
222
|
return {
|
|
183
223
|
data: finalData,
|
|
184
|
-
// <-- وصول مباشر للبيانات النهائية!
|
|
185
224
|
rawResponse: rawData,
|
|
186
|
-
// احتفظ بالاستجابة الكاملة إذا احتجت إليها
|
|
187
225
|
loading: false,
|
|
188
226
|
success: true,
|
|
189
227
|
error: null,
|
|
@@ -191,6 +229,7 @@ var processResponse = (responseOrError) => {
|
|
|
191
229
|
validationErrors: []
|
|
192
230
|
};
|
|
193
231
|
}
|
|
232
|
+
log && console.log(`****[ API STATUS 500 ]: *** ${responseOrError} *** `);
|
|
194
233
|
return {
|
|
195
234
|
data: null,
|
|
196
235
|
rawResponse: void 0,
|
|
@@ -228,6 +267,15 @@ function createApiServices(axiosInstance, endpoint) {
|
|
|
228
267
|
return processResponse(error);
|
|
229
268
|
}
|
|
230
269
|
};
|
|
270
|
+
const put = async (id, data, config) => {
|
|
271
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
|
272
|
+
try {
|
|
273
|
+
const response = await axiosInstance.put(finalUrl, data, config);
|
|
274
|
+
return processResponse(response);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
return processResponse(error);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
231
279
|
const patch = async (id, data, config) => {
|
|
232
280
|
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
|
233
281
|
try {
|
|
@@ -246,23 +294,57 @@ function createApiServices(axiosInstance, endpoint) {
|
|
|
246
294
|
return processResponse(error);
|
|
247
295
|
}
|
|
248
296
|
};
|
|
249
|
-
|
|
297
|
+
const bulkDelete = async (ids, config) => {
|
|
298
|
+
const finalUrl = config?.endpoint || `${endpoint}`;
|
|
299
|
+
try {
|
|
300
|
+
const response = await axiosInstance.delete(finalUrl, { data: { ids }, ...config });
|
|
301
|
+
return processResponse(response);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return processResponse(error);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const upload = async (file, additionalData, config) => {
|
|
307
|
+
const finalUrl = config?.endpoint || `${endpoint}`;
|
|
308
|
+
const formData = new FormData();
|
|
309
|
+
formData.append("file", file);
|
|
310
|
+
if (additionalData) {
|
|
311
|
+
Object.keys(additionalData).forEach((key) => {
|
|
312
|
+
formData.append(key, additionalData[key]);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const response = await axiosInstance.post(finalUrl, formData, {
|
|
317
|
+
...config,
|
|
318
|
+
headers: {
|
|
319
|
+
...config?.headers,
|
|
320
|
+
"Content-Type": "multipart/form-data"
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
return processResponse(response);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
return processResponse(error);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };
|
|
250
329
|
}
|
|
251
330
|
|
|
252
331
|
// src/services/actions.ts
|
|
253
|
-
function createAction(axiosInstance, method, endpoint) {
|
|
332
|
+
function createAction(axiosInstance, method, endpoint, log) {
|
|
254
333
|
return async (payload, config) => {
|
|
334
|
+
log && console.log(`*[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} **** [${payload}]** `);
|
|
255
335
|
try {
|
|
336
|
+
log && console.log(`**[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]:${endpoint} **** [action try req]** `);
|
|
256
337
|
const response = await axiosInstance.request({
|
|
257
|
-
// <--- غيرنا النوع إلى any
|
|
258
338
|
url: endpoint,
|
|
259
339
|
method,
|
|
260
340
|
...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
|
|
261
341
|
...config
|
|
262
342
|
});
|
|
263
|
-
|
|
343
|
+
log && console.log(`***[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} ***
|
|
344
|
+
${response}*** `);
|
|
345
|
+
return processResponse(response, log);
|
|
264
346
|
} catch (error) {
|
|
265
|
-
return processResponse(error);
|
|
347
|
+
return processResponse(error, log);
|
|
266
348
|
}
|
|
267
349
|
};
|
|
268
350
|
}
|
|
@@ -270,8 +352,8 @@ function createApiActions(axiosInstance, actionsConfig) {
|
|
|
270
352
|
const actions = {};
|
|
271
353
|
for (const actionName in actionsConfig) {
|
|
272
354
|
if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
|
|
273
|
-
const { method, endpoint } = actionsConfig[actionName];
|
|
274
|
-
actions[actionName] = createAction(axiosInstance, method, endpoint);
|
|
355
|
+
const { method, endpoint, log } = actionsConfig[actionName];
|
|
356
|
+
actions[actionName] = createAction(axiosInstance, method, endpoint, log);
|
|
275
357
|
}
|
|
276
358
|
}
|
|
277
359
|
return actions;
|
|
@@ -337,7 +419,6 @@ function useApi(axiosInstance, config) {
|
|
|
337
419
|
const result = await apiServices.getWithQuery(queryString, {
|
|
338
420
|
cancelTokenKey: endpoint,
|
|
339
421
|
...requestConfig
|
|
340
|
-
// <-- هنا نمرر الإعدادات الافتراضية
|
|
341
422
|
});
|
|
342
423
|
setState(result);
|
|
343
424
|
if (!result.success && onError) {
|
|
@@ -362,6 +443,19 @@ function useApi(axiosInstance, config) {
|
|
|
362
443
|
}
|
|
363
444
|
return result;
|
|
364
445
|
};
|
|
446
|
+
const putItem = async (id, item, options) => {
|
|
447
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
448
|
+
const result = await apiServices.put(id, item, options);
|
|
449
|
+
if (result.success) {
|
|
450
|
+
if (refetchAfterChange) await fetchData();
|
|
451
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
452
|
+
if (onSuccess) onSuccess(result.message || "Item replaced successfully!", result.data);
|
|
453
|
+
} else {
|
|
454
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
455
|
+
if (onError) onError(result.message || "Replace failed", result.error || void 0);
|
|
456
|
+
}
|
|
457
|
+
return result;
|
|
458
|
+
};
|
|
365
459
|
const updateItem = async (id, updatedItem, options) => {
|
|
366
460
|
setState((prev) => ({ ...prev, loading: true }));
|
|
367
461
|
const result = await apiServices.patch(id, updatedItem, options);
|
|
@@ -388,19 +482,39 @@ function useApi(axiosInstance, config) {
|
|
|
388
482
|
}
|
|
389
483
|
return result;
|
|
390
484
|
};
|
|
485
|
+
const bulkDeleteItem = async (ids, options) => {
|
|
486
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
487
|
+
const result = await apiServices.bulkDelete(ids, options);
|
|
488
|
+
if (result.success) {
|
|
489
|
+
if (refetchAfterChange) await fetchData();
|
|
490
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
491
|
+
if (onSuccess) onSuccess(result.message || "Items deleted successfully!");
|
|
492
|
+
} else {
|
|
493
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
494
|
+
if (onError) onError(result.message || "Bulk delete failed", result.error || void 0);
|
|
495
|
+
}
|
|
496
|
+
return result;
|
|
497
|
+
};
|
|
498
|
+
const uploadFile = async (file, additionalData, options) => {
|
|
499
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
500
|
+
const result = await apiServices.upload(file, additionalData, options);
|
|
501
|
+
if (result.success) {
|
|
502
|
+
if (refetchAfterChange && options?.refetch !== false) await fetchData();
|
|
503
|
+
else setState((prev) => ({ ...prev, loading: false }));
|
|
504
|
+
if (onSuccess) onSuccess(result.message || "File uploaded successfully!", result.data);
|
|
505
|
+
} else {
|
|
506
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
|
507
|
+
if (onError) onError(result.message || "Upload failed", result.error || void 0);
|
|
508
|
+
}
|
|
509
|
+
return result;
|
|
510
|
+
};
|
|
391
511
|
const setPage = (page) => setQueryOptions((prev) => ({ ...prev, page }));
|
|
392
512
|
const setLimit = (limit) => setQueryOptions((prev) => ({ ...prev, limit, page: 1 }));
|
|
393
513
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
|
394
514
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
|
395
515
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
|
396
516
|
const setQueryParam = (key, value) => {
|
|
397
|
-
setQueryOptions((prev) => {
|
|
398
|
-
const newQuery = { ...prev, [key]: value };
|
|
399
|
-
if (key !== "page") {
|
|
400
|
-
newQuery.page = 1;
|
|
401
|
-
}
|
|
402
|
-
return newQuery;
|
|
403
|
-
});
|
|
517
|
+
setQueryOptions((prev) => ({ ...prev, [key]: value, page: key !== "page" ? 1 : prev.page }));
|
|
404
518
|
};
|
|
405
519
|
const resetQuery = () => setQueryOptions(initialQuery);
|
|
406
520
|
return {
|
|
@@ -409,8 +523,14 @@ function useApi(axiosInstance, config) {
|
|
|
409
523
|
actions: {
|
|
410
524
|
fetch: fetchData,
|
|
411
525
|
create: createItem,
|
|
526
|
+
put: putItem,
|
|
527
|
+
// <-- NEW
|
|
412
528
|
update: updateItem,
|
|
413
|
-
remove: deleteItem
|
|
529
|
+
remove: deleteItem,
|
|
530
|
+
bulkRemove: bulkDeleteItem,
|
|
531
|
+
// <-- NEW
|
|
532
|
+
upload: uploadFile
|
|
533
|
+
// <-- NEW
|
|
414
534
|
},
|
|
415
535
|
query: {
|
|
416
536
|
options: queryOptions,
|
|
@@ -421,7 +541,6 @@ function useApi(axiosInstance, config) {
|
|
|
421
541
|
setSorting,
|
|
422
542
|
setFilters,
|
|
423
543
|
setQueryParam,
|
|
424
|
-
// <-- NEW
|
|
425
544
|
reset: resetQuery
|
|
426
545
|
}
|
|
427
546
|
};
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "api-core-lib",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A flexible and powerful API client library for modern web applications.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
10
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"axios",
|
|
14
|
-
"api",
|
|
15
|
-
"react",
|
|
16
|
-
"nextjs",
|
|
17
|
-
"typescript",
|
|
18
|
-
"state-management"
|
|
19
|
-
],
|
|
20
|
-
"author": "Your Name",
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"peerDependencies": {
|
|
23
|
-
"react": ">=17.0.0"
|
|
24
|
-
},
|
|
25
|
-
"dependencies": {
|
|
26
|
-
"axios": "^1.6.8",
|
|
27
|
-
"axios-retry": "^4.1.0",
|
|
28
|
-
"uuid": "^9.0.1"
|
|
29
|
-
},
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@types/react": "^18.3.2",
|
|
32
|
-
"@types/uuid": "^9.0.8",
|
|
33
|
-
"react": "^18.3.1",
|
|
34
|
-
"tsup": "^8.0.2",
|
|
35
|
-
"typescript": "^5.4.5"
|
|
36
|
-
},
|
|
37
|
-
"files": [
|
|
38
|
-
"dist"
|
|
39
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"name": "api-core-lib",
|
|
3
|
+
"version": "5.5.4",
|
|
4
|
+
"description": "A flexible and powerful API client library for modern web applications.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
10
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"axios",
|
|
14
|
+
"api",
|
|
15
|
+
"react",
|
|
16
|
+
"nextjs",
|
|
17
|
+
"typescript",
|
|
18
|
+
"state-management"
|
|
19
|
+
],
|
|
20
|
+
"author": "Your Name",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": ">=17.0.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"axios": "^1.6.8",
|
|
27
|
+
"axios-retry": "^4.1.0",
|
|
28
|
+
"uuid": "^9.0.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/react": "^18.3.2",
|
|
32
|
+
"@types/uuid": "^9.0.8",
|
|
33
|
+
"react": "^18.3.1",
|
|
34
|
+
"tsup": "^8.0.2",
|
|
35
|
+
"typescript": "^5.4.5"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
]
|
|
40
40
|
}
|