api-core-lib 3.3.2 → 4.3.3
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 +63 -24
- package/dist/index.d.ts +63 -24
- package/dist/index.js +44 -15
- package/dist/index.mjs +44 -15
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
@@ -61,6 +61,7 @@ interface RequestConfig extends AxiosRequestConfig {
|
|
61
61
|
cancelTokenKey?: string;
|
62
62
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
63
63
|
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
64
|
+
isPublic?: boolean;
|
64
65
|
}
|
65
66
|
/**
|
66
67
|
* يمثل خيارات الاستعلام للترقيم والبحث والفرز.
|
@@ -75,19 +76,6 @@ interface PaginateQueryOptions {
|
|
75
76
|
}[];
|
76
77
|
filter?: Record<string, any>;
|
77
78
|
}
|
78
|
-
/**
|
79
|
-
* واجهة لتهيئة الهوك `useApi`.
|
80
|
-
* @template T نوع البيانات التي يتعامل معها الهوك.
|
81
|
-
*/
|
82
|
-
interface UseApiConfig<T> {
|
83
|
-
endpoint: string;
|
84
|
-
initialData?: T | T[];
|
85
|
-
initialQuery?: PaginateQueryOptions;
|
86
|
-
enabled?: boolean;
|
87
|
-
refetchAfterChange?: boolean;
|
88
|
-
onSuccess?: (message: string, data?: T) => void;
|
89
|
-
onError?: (message: string, error?: ApiError) => void;
|
90
|
-
}
|
91
79
|
/**
|
92
80
|
* واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
|
93
81
|
*/
|
@@ -174,6 +162,49 @@ interface StandardResponse<T> {
|
|
174
162
|
message?: string;
|
175
163
|
validationErrors?: ValidationError[];
|
176
164
|
}
|
165
|
+
/**
|
166
|
+
* NEW: واجهة استعلام مرنة تسمح بالبارامترات القياسية (للفلترة والترقيم)
|
167
|
+
* بالإضافة إلى أي بارامترات مخصصة أخرى عبر الـ index signature.
|
168
|
+
* @example
|
169
|
+
* { page: 1, limit: 10, search: 'term', status: 'published', authorId: 123 }
|
170
|
+
*/
|
171
|
+
interface QueryOptions {
|
172
|
+
page?: number;
|
173
|
+
limit?: number;
|
174
|
+
search?: string;
|
175
|
+
sortBy?: {
|
176
|
+
key: string;
|
177
|
+
direction: 'asc' | 'desc';
|
178
|
+
}[];
|
179
|
+
filter?: Record<string, any>;
|
180
|
+
[key: string]: any;
|
181
|
+
}
|
182
|
+
/**
|
183
|
+
* NEW: واجهة لتمرير خيارات إضافية لدوال الأكشن (create, update, remove).
|
184
|
+
*/
|
185
|
+
interface ActionOptions {
|
186
|
+
/**
|
187
|
+
* يسمح لك بتجاوز الـ endpoint الافتراضي المحدد في الهوك.
|
188
|
+
* مفيد لإرسال طلبات لنقاط نهاية متخصصة.
|
189
|
+
* @example
|
190
|
+
* // لتحديث عنصر وتغيير حالته عبر مسار مختلف
|
191
|
+
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
192
|
+
*/
|
193
|
+
endpoint?: string;
|
194
|
+
}
|
195
|
+
/**
|
196
|
+
* واجهة لتهيئة الهوك `useApi`.
|
197
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
198
|
+
*/
|
199
|
+
interface UseApiConfig<T> {
|
200
|
+
endpoint: string;
|
201
|
+
initialData?: T | T[];
|
202
|
+
initialQuery?: QueryOptions;
|
203
|
+
enabled?: boolean;
|
204
|
+
refetchAfterChange?: boolean;
|
205
|
+
onSuccess?: (message: string, data?: T) => void;
|
206
|
+
onError?: (message: string, error?: ApiError) => void;
|
207
|
+
}
|
177
208
|
|
178
209
|
/**
|
179
210
|
* @file src/core/client.ts
|
@@ -185,12 +216,13 @@ interface StandardResponse<T> {
|
|
185
216
|
|
186
217
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
187
218
|
|
219
|
+
type CrudRequestConfig = RequestConfig & ActionOptions;
|
188
220
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
189
221
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
190
222
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
191
|
-
post: (data: Partial<T>, config?:
|
192
|
-
patch: (id: string, data: Partial<T>, config?:
|
193
|
-
remove: (id: string, config?:
|
223
|
+
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
224
|
+
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
225
|
+
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
194
226
|
};
|
195
227
|
|
196
228
|
/**
|
@@ -201,7 +233,13 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
201
233
|
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
202
234
|
*/
|
203
235
|
|
204
|
-
|
236
|
+
/**
|
237
|
+
* يبني سلسلة استعلام (query string) من كائن خيارات مرن.
|
238
|
+
* يتعامل مع البارامترات القياسية والمخصصة.
|
239
|
+
* @param query - كائن من نوع QueryOptions.
|
240
|
+
* @returns سلسلة استعلام جاهزة للإضافة للرابط.
|
241
|
+
*/
|
242
|
+
declare function buildPaginateQuery(query: QueryOptions): string;
|
205
243
|
|
206
244
|
/**
|
207
245
|
* Defines a single custom API action.
|
@@ -263,14 +301,14 @@ declare function useApi<T extends {
|
|
263
301
|
state: StandardResponse<T | T[]>;
|
264
302
|
setState: react.Dispatch<react.SetStateAction<StandardResponse<T | T[]>>>;
|
265
303
|
actions: {
|
266
|
-
fetch: () => Promise<void>;
|
267
|
-
create: (newItem: Partial<T
|
268
|
-
update: (id: string, updatedItem: Partial<T
|
269
|
-
remove: (id: string) => Promise<StandardResponse<any>>;
|
304
|
+
fetch: (options?: QueryOptions) => Promise<void>;
|
305
|
+
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
306
|
+
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
307
|
+
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
270
308
|
};
|
271
309
|
query: {
|
272
|
-
options:
|
273
|
-
setOptions: react.Dispatch<react.SetStateAction<
|
310
|
+
options: QueryOptions;
|
311
|
+
setOptions: react.Dispatch<react.SetStateAction<QueryOptions>>;
|
274
312
|
setPage: (page: number) => void;
|
275
313
|
setLimit: (limit: number) => void;
|
276
314
|
setSearchTerm: (search: string) => void;
|
@@ -279,8 +317,9 @@ declare function useApi<T extends {
|
|
279
317
|
direction: "asc" | "desc";
|
280
318
|
}[]) => void;
|
281
319
|
setFilters: (filter: Record<string, any>) => void;
|
320
|
+
setQueryParam: (key: string, value: any) => void;
|
282
321
|
reset: () => void;
|
283
322
|
};
|
284
323
|
};
|
285
324
|
|
286
|
-
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
325
|
+
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 };
|
package/dist/index.d.ts
CHANGED
@@ -61,6 +61,7 @@ interface RequestConfig extends AxiosRequestConfig {
|
|
61
61
|
cancelTokenKey?: string;
|
62
62
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
63
63
|
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
64
|
+
isPublic?: boolean;
|
64
65
|
}
|
65
66
|
/**
|
66
67
|
* يمثل خيارات الاستعلام للترقيم والبحث والفرز.
|
@@ -75,19 +76,6 @@ interface PaginateQueryOptions {
|
|
75
76
|
}[];
|
76
77
|
filter?: Record<string, any>;
|
77
78
|
}
|
78
|
-
/**
|
79
|
-
* واجهة لتهيئة الهوك `useApi`.
|
80
|
-
* @template T نوع البيانات التي يتعامل معها الهوك.
|
81
|
-
*/
|
82
|
-
interface UseApiConfig<T> {
|
83
|
-
endpoint: string;
|
84
|
-
initialData?: T | T[];
|
85
|
-
initialQuery?: PaginateQueryOptions;
|
86
|
-
enabled?: boolean;
|
87
|
-
refetchAfterChange?: boolean;
|
88
|
-
onSuccess?: (message: string, data?: T) => void;
|
89
|
-
onError?: (message: string, error?: ApiError) => void;
|
90
|
-
}
|
91
79
|
/**
|
92
80
|
* واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
|
93
81
|
*/
|
@@ -174,6 +162,49 @@ interface StandardResponse<T> {
|
|
174
162
|
message?: string;
|
175
163
|
validationErrors?: ValidationError[];
|
176
164
|
}
|
165
|
+
/**
|
166
|
+
* NEW: واجهة استعلام مرنة تسمح بالبارامترات القياسية (للفلترة والترقيم)
|
167
|
+
* بالإضافة إلى أي بارامترات مخصصة أخرى عبر الـ index signature.
|
168
|
+
* @example
|
169
|
+
* { page: 1, limit: 10, search: 'term', status: 'published', authorId: 123 }
|
170
|
+
*/
|
171
|
+
interface QueryOptions {
|
172
|
+
page?: number;
|
173
|
+
limit?: number;
|
174
|
+
search?: string;
|
175
|
+
sortBy?: {
|
176
|
+
key: string;
|
177
|
+
direction: 'asc' | 'desc';
|
178
|
+
}[];
|
179
|
+
filter?: Record<string, any>;
|
180
|
+
[key: string]: any;
|
181
|
+
}
|
182
|
+
/**
|
183
|
+
* NEW: واجهة لتمرير خيارات إضافية لدوال الأكشن (create, update, remove).
|
184
|
+
*/
|
185
|
+
interface ActionOptions {
|
186
|
+
/**
|
187
|
+
* يسمح لك بتجاوز الـ endpoint الافتراضي المحدد في الهوك.
|
188
|
+
* مفيد لإرسال طلبات لنقاط نهاية متخصصة.
|
189
|
+
* @example
|
190
|
+
* // لتحديث عنصر وتغيير حالته عبر مسار مختلف
|
191
|
+
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
192
|
+
*/
|
193
|
+
endpoint?: string;
|
194
|
+
}
|
195
|
+
/**
|
196
|
+
* واجهة لتهيئة الهوك `useApi`.
|
197
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
198
|
+
*/
|
199
|
+
interface UseApiConfig<T> {
|
200
|
+
endpoint: string;
|
201
|
+
initialData?: T | T[];
|
202
|
+
initialQuery?: QueryOptions;
|
203
|
+
enabled?: boolean;
|
204
|
+
refetchAfterChange?: boolean;
|
205
|
+
onSuccess?: (message: string, data?: T) => void;
|
206
|
+
onError?: (message: string, error?: ApiError) => void;
|
207
|
+
}
|
177
208
|
|
178
209
|
/**
|
179
210
|
* @file src/core/client.ts
|
@@ -185,12 +216,13 @@ interface StandardResponse<T> {
|
|
185
216
|
|
186
217
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
187
218
|
|
219
|
+
type CrudRequestConfig = RequestConfig & ActionOptions;
|
188
220
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
189
221
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
190
222
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
191
|
-
post: (data: Partial<T>, config?:
|
192
|
-
patch: (id: string, data: Partial<T>, config?:
|
193
|
-
remove: (id: string, config?:
|
223
|
+
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
224
|
+
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
225
|
+
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
194
226
|
};
|
195
227
|
|
196
228
|
/**
|
@@ -201,7 +233,13 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
201
233
|
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
202
234
|
*/
|
203
235
|
|
204
|
-
|
236
|
+
/**
|
237
|
+
* يبني سلسلة استعلام (query string) من كائن خيارات مرن.
|
238
|
+
* يتعامل مع البارامترات القياسية والمخصصة.
|
239
|
+
* @param query - كائن من نوع QueryOptions.
|
240
|
+
* @returns سلسلة استعلام جاهزة للإضافة للرابط.
|
241
|
+
*/
|
242
|
+
declare function buildPaginateQuery(query: QueryOptions): string;
|
205
243
|
|
206
244
|
/**
|
207
245
|
* Defines a single custom API action.
|
@@ -263,14 +301,14 @@ declare function useApi<T extends {
|
|
263
301
|
state: StandardResponse<T | T[]>;
|
264
302
|
setState: react.Dispatch<react.SetStateAction<StandardResponse<T | T[]>>>;
|
265
303
|
actions: {
|
266
|
-
fetch: () => Promise<void>;
|
267
|
-
create: (newItem: Partial<T
|
268
|
-
update: (id: string, updatedItem: Partial<T
|
269
|
-
remove: (id: string) => Promise<StandardResponse<any>>;
|
304
|
+
fetch: (options?: QueryOptions) => Promise<void>;
|
305
|
+
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
306
|
+
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
307
|
+
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
270
308
|
};
|
271
309
|
query: {
|
272
|
-
options:
|
273
|
-
setOptions: react.Dispatch<react.SetStateAction<
|
310
|
+
options: QueryOptions;
|
311
|
+
setOptions: react.Dispatch<react.SetStateAction<QueryOptions>>;
|
274
312
|
setPage: (page: number) => void;
|
275
313
|
setLimit: (limit: number) => void;
|
276
314
|
setSearchTerm: (search: string) => void;
|
@@ -279,8 +317,9 @@ declare function useApi<T extends {
|
|
279
317
|
direction: "asc" | "desc";
|
280
318
|
}[]) => void;
|
281
319
|
setFilters: (filter: Record<string, any>) => void;
|
320
|
+
setQueryParam: (key: string, value: any) => void;
|
282
321
|
reset: () => void;
|
283
322
|
};
|
284
323
|
};
|
285
324
|
|
286
|
-
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
325
|
+
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 };
|
package/dist/index.js
CHANGED
@@ -106,7 +106,10 @@ function createApiClient(config) {
|
|
106
106
|
let tokenRefreshPromise = null;
|
107
107
|
axiosInstance.interceptors.request.use(async (req) => {
|
108
108
|
req.headers["X-Request-ID"] = (0, import_uuid.v4)();
|
109
|
-
if (req.
|
109
|
+
if (req.isPublic) {
|
110
|
+
console.log(`[API Core] Skipping token for public request: ${req.url}`);
|
111
|
+
return req;
|
112
|
+
}
|
110
113
|
if (tokenRefreshPromise) {
|
111
114
|
await tokenRefreshPromise;
|
112
115
|
}
|
@@ -125,6 +128,9 @@ function createApiClient(config) {
|
|
125
128
|
if (tokens.accessToken && !tokenManager.isHttpOnly()) {
|
126
129
|
const tokenType = tokens.tokenType || "Bearer";
|
127
130
|
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
131
|
+
console.log(`[API Core] Token attached to request: ${req.url}`);
|
132
|
+
} else {
|
133
|
+
console.warn(`[API Core] No token attached for request: ${req.url}`);
|
128
134
|
}
|
129
135
|
return req;
|
130
136
|
}, (error) => Promise.reject(error));
|
@@ -164,6 +170,7 @@ var import_axios2 = __toESM(require("axios"));
|
|
164
170
|
function buildPaginateQuery(query) {
|
165
171
|
if (!query) return "";
|
166
172
|
const params = new URLSearchParams();
|
173
|
+
const standardKeys = /* @__PURE__ */ new Set(["page", "limit", "search", "sortBy", "filter"]);
|
167
174
|
if (query.page) params.append("page", query.page.toString());
|
168
175
|
if (query.limit) params.append("limit", query.limit.toString());
|
169
176
|
if (query.search) params.append("search", query.search);
|
@@ -175,6 +182,14 @@ function buildPaginateQuery(query) {
|
|
175
182
|
params.append(`filter.${field}`, String(value));
|
176
183
|
});
|
177
184
|
}
|
185
|
+
for (const key in query) {
|
186
|
+
if (Object.prototype.hasOwnProperty.call(query, key) && !standardKeys.has(key)) {
|
187
|
+
const value = query[key];
|
188
|
+
if (value !== void 0 && value !== null) {
|
189
|
+
params.append(key, String(value));
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
178
193
|
const queryString = params.toString();
|
179
194
|
return queryString ? `?${queryString}` : "";
|
180
195
|
}
|
@@ -244,24 +259,27 @@ function createApiServices(axiosInstance, endpoint) {
|
|
244
259
|
}
|
245
260
|
};
|
246
261
|
const post = async (data, config) => {
|
262
|
+
const finalUrl = config?.endpoint || endpoint;
|
247
263
|
try {
|
248
|
-
const response = await axiosInstance.post(
|
264
|
+
const response = await axiosInstance.post(finalUrl, data, config);
|
249
265
|
return processResponse(response);
|
250
266
|
} catch (error) {
|
251
267
|
return processResponse(error);
|
252
268
|
}
|
253
269
|
};
|
254
270
|
const patch = async (id, data, config) => {
|
271
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
255
272
|
try {
|
256
|
-
const response = await axiosInstance.patch(
|
273
|
+
const response = await axiosInstance.patch(finalUrl, data, config);
|
257
274
|
return processResponse(response);
|
258
275
|
} catch (error) {
|
259
276
|
return processResponse(error);
|
260
277
|
}
|
261
278
|
};
|
262
279
|
const remove = async (id, config) => {
|
280
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
263
281
|
try {
|
264
|
-
const response = await axiosInstance.delete(
|
282
|
+
const response = await axiosInstance.delete(finalUrl, config);
|
265
283
|
return processResponse(response);
|
266
284
|
} catch (error) {
|
267
285
|
return processResponse(error);
|
@@ -335,15 +353,15 @@ function useApi(axiosInstance, config) {
|
|
335
353
|
const {
|
336
354
|
endpoint,
|
337
355
|
initialData,
|
338
|
-
initialQuery = {
|
356
|
+
initialQuery = { limit: 10 },
|
339
357
|
enabled = true,
|
340
358
|
refetchAfterChange = true,
|
359
|
+
// تم تعديل القيمة الافتراضية لتكون أكثر شيوعًا
|
341
360
|
onSuccess,
|
342
361
|
onError
|
343
362
|
} = config;
|
344
363
|
const [state, setState] = (0, import_react.useState)({
|
345
364
|
data: initialData || null,
|
346
|
-
// <--- التغيير هنا
|
347
365
|
rawResponse: null,
|
348
366
|
loading: enabled,
|
349
367
|
error: null,
|
@@ -351,9 +369,10 @@ function useApi(axiosInstance, config) {
|
|
351
369
|
});
|
352
370
|
const [queryOptions, setQueryOptions] = (0, import_react.useState)(initialQuery);
|
353
371
|
const apiServices = (0, import_react.useRef)(createApiServices(axiosInstance, endpoint)).current;
|
354
|
-
const fetchData = (0, import_react.useCallback)(async () => {
|
372
|
+
const fetchData = (0, import_react.useCallback)(async (options) => {
|
373
|
+
const currentQuery = options || queryOptions;
|
355
374
|
setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
|
356
|
-
const queryString = buildPaginateQuery(
|
375
|
+
const queryString = buildPaginateQuery(currentQuery);
|
357
376
|
const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
|
358
377
|
setState(result);
|
359
378
|
if (!result.success && onError) {
|
@@ -365,9 +384,9 @@ function useApi(axiosInstance, config) {
|
|
365
384
|
fetchData();
|
366
385
|
}
|
367
386
|
}, [enabled, queryOptions]);
|
368
|
-
const createItem = async (newItem) => {
|
387
|
+
const createItem = async (newItem, options) => {
|
369
388
|
setState((prev) => ({ ...prev, loading: true }));
|
370
|
-
const result = await apiServices.post(newItem);
|
389
|
+
const result = await apiServices.post(newItem, options);
|
371
390
|
if (result.success) {
|
372
391
|
if (refetchAfterChange) await fetchData();
|
373
392
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -378,9 +397,9 @@ function useApi(axiosInstance, config) {
|
|
378
397
|
}
|
379
398
|
return result;
|
380
399
|
};
|
381
|
-
const updateItem = async (id, updatedItem) => {
|
400
|
+
const updateItem = async (id, updatedItem, options) => {
|
382
401
|
setState((prev) => ({ ...prev, loading: true }));
|
383
|
-
const result = await apiServices.patch(id, updatedItem);
|
402
|
+
const result = await apiServices.patch(id, updatedItem, options);
|
384
403
|
if (result.success) {
|
385
404
|
if (refetchAfterChange) await fetchData();
|
386
405
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -391,9 +410,9 @@ function useApi(axiosInstance, config) {
|
|
391
410
|
}
|
392
411
|
return result;
|
393
412
|
};
|
394
|
-
const deleteItem = async (id) => {
|
413
|
+
const deleteItem = async (id, options) => {
|
395
414
|
setState((prev) => ({ ...prev, loading: true }));
|
396
|
-
const result = await apiServices.remove(id);
|
415
|
+
const result = await apiServices.remove(id, options);
|
397
416
|
if (result.success) {
|
398
417
|
if (refetchAfterChange) await fetchData();
|
399
418
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -409,11 +428,19 @@ function useApi(axiosInstance, config) {
|
|
409
428
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
410
429
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
411
430
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
431
|
+
const setQueryParam = (key, value) => {
|
432
|
+
setQueryOptions((prev) => {
|
433
|
+
const newQuery = { ...prev, [key]: value };
|
434
|
+
if (key !== "page") {
|
435
|
+
newQuery.page = 1;
|
436
|
+
}
|
437
|
+
return newQuery;
|
438
|
+
});
|
439
|
+
};
|
412
440
|
const resetQuery = () => setQueryOptions(initialQuery);
|
413
441
|
return {
|
414
442
|
state,
|
415
443
|
setState,
|
416
|
-
// Exposing setState for direct manipulation or connection to Redux
|
417
444
|
actions: {
|
418
445
|
fetch: fetchData,
|
419
446
|
create: createItem,
|
@@ -428,6 +455,8 @@ function useApi(axiosInstance, config) {
|
|
428
455
|
setSearchTerm,
|
429
456
|
setSorting,
|
430
457
|
setFilters,
|
458
|
+
setQueryParam,
|
459
|
+
// <-- NEW
|
431
460
|
reset: resetQuery
|
432
461
|
}
|
433
462
|
};
|
package/dist/index.mjs
CHANGED
@@ -64,7 +64,10 @@ function createApiClient(config) {
|
|
64
64
|
let tokenRefreshPromise = null;
|
65
65
|
axiosInstance.interceptors.request.use(async (req) => {
|
66
66
|
req.headers["X-Request-ID"] = uuidv4();
|
67
|
-
if (req.
|
67
|
+
if (req.isPublic) {
|
68
|
+
console.log(`[API Core] Skipping token for public request: ${req.url}`);
|
69
|
+
return req;
|
70
|
+
}
|
68
71
|
if (tokenRefreshPromise) {
|
69
72
|
await tokenRefreshPromise;
|
70
73
|
}
|
@@ -83,6 +86,9 @@ function createApiClient(config) {
|
|
83
86
|
if (tokens.accessToken && !tokenManager.isHttpOnly()) {
|
84
87
|
const tokenType = tokens.tokenType || "Bearer";
|
85
88
|
req.headers.Authorization = `${tokenType} ${tokens.accessToken}`;
|
89
|
+
console.log(`[API Core] Token attached to request: ${req.url}`);
|
90
|
+
} else {
|
91
|
+
console.warn(`[API Core] No token attached for request: ${req.url}`);
|
86
92
|
}
|
87
93
|
return req;
|
88
94
|
}, (error) => Promise.reject(error));
|
@@ -122,6 +128,7 @@ import axios2 from "axios";
|
|
122
128
|
function buildPaginateQuery(query) {
|
123
129
|
if (!query) return "";
|
124
130
|
const params = new URLSearchParams();
|
131
|
+
const standardKeys = /* @__PURE__ */ new Set(["page", "limit", "search", "sortBy", "filter"]);
|
125
132
|
if (query.page) params.append("page", query.page.toString());
|
126
133
|
if (query.limit) params.append("limit", query.limit.toString());
|
127
134
|
if (query.search) params.append("search", query.search);
|
@@ -133,6 +140,14 @@ function buildPaginateQuery(query) {
|
|
133
140
|
params.append(`filter.${field}`, String(value));
|
134
141
|
});
|
135
142
|
}
|
143
|
+
for (const key in query) {
|
144
|
+
if (Object.prototype.hasOwnProperty.call(query, key) && !standardKeys.has(key)) {
|
145
|
+
const value = query[key];
|
146
|
+
if (value !== void 0 && value !== null) {
|
147
|
+
params.append(key, String(value));
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
136
151
|
const queryString = params.toString();
|
137
152
|
return queryString ? `?${queryString}` : "";
|
138
153
|
}
|
@@ -202,24 +217,27 @@ function createApiServices(axiosInstance, endpoint) {
|
|
202
217
|
}
|
203
218
|
};
|
204
219
|
const post = async (data, config) => {
|
220
|
+
const finalUrl = config?.endpoint || endpoint;
|
205
221
|
try {
|
206
|
-
const response = await axiosInstance.post(
|
222
|
+
const response = await axiosInstance.post(finalUrl, data, config);
|
207
223
|
return processResponse(response);
|
208
224
|
} catch (error) {
|
209
225
|
return processResponse(error);
|
210
226
|
}
|
211
227
|
};
|
212
228
|
const patch = async (id, data, config) => {
|
229
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
213
230
|
try {
|
214
|
-
const response = await axiosInstance.patch(
|
231
|
+
const response = await axiosInstance.patch(finalUrl, data, config);
|
215
232
|
return processResponse(response);
|
216
233
|
} catch (error) {
|
217
234
|
return processResponse(error);
|
218
235
|
}
|
219
236
|
};
|
220
237
|
const remove = async (id, config) => {
|
238
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
221
239
|
try {
|
222
|
-
const response = await axiosInstance.delete(
|
240
|
+
const response = await axiosInstance.delete(finalUrl, config);
|
223
241
|
return processResponse(response);
|
224
242
|
} catch (error) {
|
225
243
|
return processResponse(error);
|
@@ -293,15 +311,15 @@ function useApi(axiosInstance, config) {
|
|
293
311
|
const {
|
294
312
|
endpoint,
|
295
313
|
initialData,
|
296
|
-
initialQuery = {
|
314
|
+
initialQuery = { limit: 10 },
|
297
315
|
enabled = true,
|
298
316
|
refetchAfterChange = true,
|
317
|
+
// تم تعديل القيمة الافتراضية لتكون أكثر شيوعًا
|
299
318
|
onSuccess,
|
300
319
|
onError
|
301
320
|
} = config;
|
302
321
|
const [state, setState] = useState({
|
303
322
|
data: initialData || null,
|
304
|
-
// <--- التغيير هنا
|
305
323
|
rawResponse: null,
|
306
324
|
loading: enabled,
|
307
325
|
error: null,
|
@@ -309,9 +327,10 @@ function useApi(axiosInstance, config) {
|
|
309
327
|
});
|
310
328
|
const [queryOptions, setQueryOptions] = useState(initialQuery);
|
311
329
|
const apiServices = useRef(createApiServices(axiosInstance, endpoint)).current;
|
312
|
-
const fetchData = useCallback(async () => {
|
330
|
+
const fetchData = useCallback(async (options) => {
|
331
|
+
const currentQuery = options || queryOptions;
|
313
332
|
setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
|
314
|
-
const queryString = buildPaginateQuery(
|
333
|
+
const queryString = buildPaginateQuery(currentQuery);
|
315
334
|
const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
|
316
335
|
setState(result);
|
317
336
|
if (!result.success && onError) {
|
@@ -323,9 +342,9 @@ function useApi(axiosInstance, config) {
|
|
323
342
|
fetchData();
|
324
343
|
}
|
325
344
|
}, [enabled, queryOptions]);
|
326
|
-
const createItem = async (newItem) => {
|
345
|
+
const createItem = async (newItem, options) => {
|
327
346
|
setState((prev) => ({ ...prev, loading: true }));
|
328
|
-
const result = await apiServices.post(newItem);
|
347
|
+
const result = await apiServices.post(newItem, options);
|
329
348
|
if (result.success) {
|
330
349
|
if (refetchAfterChange) await fetchData();
|
331
350
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -336,9 +355,9 @@ function useApi(axiosInstance, config) {
|
|
336
355
|
}
|
337
356
|
return result;
|
338
357
|
};
|
339
|
-
const updateItem = async (id, updatedItem) => {
|
358
|
+
const updateItem = async (id, updatedItem, options) => {
|
340
359
|
setState((prev) => ({ ...prev, loading: true }));
|
341
|
-
const result = await apiServices.patch(id, updatedItem);
|
360
|
+
const result = await apiServices.patch(id, updatedItem, options);
|
342
361
|
if (result.success) {
|
343
362
|
if (refetchAfterChange) await fetchData();
|
344
363
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -349,9 +368,9 @@ function useApi(axiosInstance, config) {
|
|
349
368
|
}
|
350
369
|
return result;
|
351
370
|
};
|
352
|
-
const deleteItem = async (id) => {
|
371
|
+
const deleteItem = async (id, options) => {
|
353
372
|
setState((prev) => ({ ...prev, loading: true }));
|
354
|
-
const result = await apiServices.remove(id);
|
373
|
+
const result = await apiServices.remove(id, options);
|
355
374
|
if (result.success) {
|
356
375
|
if (refetchAfterChange) await fetchData();
|
357
376
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -367,11 +386,19 @@ function useApi(axiosInstance, config) {
|
|
367
386
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
368
387
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
369
388
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
389
|
+
const setQueryParam = (key, value) => {
|
390
|
+
setQueryOptions((prev) => {
|
391
|
+
const newQuery = { ...prev, [key]: value };
|
392
|
+
if (key !== "page") {
|
393
|
+
newQuery.page = 1;
|
394
|
+
}
|
395
|
+
return newQuery;
|
396
|
+
});
|
397
|
+
};
|
370
398
|
const resetQuery = () => setQueryOptions(initialQuery);
|
371
399
|
return {
|
372
400
|
state,
|
373
401
|
setState,
|
374
|
-
// Exposing setState for direct manipulation or connection to Redux
|
375
402
|
actions: {
|
376
403
|
fetch: fetchData,
|
377
404
|
create: createItem,
|
@@ -386,6 +413,8 @@ function useApi(axiosInstance, config) {
|
|
386
413
|
setSearchTerm,
|
387
414
|
setSorting,
|
388
415
|
setFilters,
|
416
|
+
setQueryParam,
|
417
|
+
// <-- NEW
|
389
418
|
reset: resetQuery
|
390
419
|
}
|
391
420
|
};
|