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 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?: RequestConfig) => Promise<StandardResponse<T>>;
192
- patch: (id: string, data: Partial<T>, config?: RequestConfig) => Promise<StandardResponse<T>>;
193
- remove: (id: string, config?: RequestConfig) => Promise<StandardResponse<any>>;
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
- declare function buildPaginateQuery(query: PaginateQueryOptions): string;
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>) => Promise<StandardResponse<T>>;
268
- update: (id: string, updatedItem: Partial<T>) => Promise<StandardResponse<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: PaginateQueryOptions;
273
- setOptions: react.Dispatch<react.SetStateAction<PaginateQueryOptions>>;
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?: RequestConfig) => Promise<StandardResponse<T>>;
192
- patch: (id: string, data: Partial<T>, config?: RequestConfig) => Promise<StandardResponse<T>>;
193
- remove: (id: string, config?: RequestConfig) => Promise<StandardResponse<any>>;
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
- declare function buildPaginateQuery(query: PaginateQueryOptions): string;
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>) => Promise<StandardResponse<T>>;
268
- update: (id: string, updatedItem: Partial<T>) => Promise<StandardResponse<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: PaginateQueryOptions;
273
- setOptions: react.Dispatch<react.SetStateAction<PaginateQueryOptions>>;
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.url?.includes("/auth/")) return 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(endpoint, data, config);
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(`${endpoint}/${id}`, data, config);
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(`${endpoint}/${id}`, config);
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 = { page: 1, limit: 10 },
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(queryOptions);
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.url?.includes("/auth/")) return 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(endpoint, data, config);
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(`${endpoint}/${id}`, data, config);
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(`${endpoint}/${id}`, config);
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 = { page: 1, limit: 10 },
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(queryOptions);
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "3.3.2",
3
+ "version": "4.3.3",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",