api-core-lib 2.2.2 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, AxiosResponse } from 'axios';
1
+ import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method, AxiosResponse } from 'axios';
2
2
  import * as react from 'react';
3
3
 
4
4
  /**
@@ -18,15 +18,6 @@ interface PaginationMeta {
18
18
  currentPage: number;
19
19
  totalPages: number;
20
20
  }
21
- /**
22
- * يمثل بنية الاستجابة القياسية من الـ API.
23
- * @template T نوع البيانات الأساسية في الاستجابة.
24
- */
25
- interface ApiResponse<T = any> {
26
- data: T;
27
- message?: string;
28
- meta?: PaginationMeta;
29
- }
30
21
  /**
31
22
  * يمثل خطأ التحقق من صحة حقل معين.
32
23
  */
@@ -44,18 +35,6 @@ interface ApiError {
44
35
  errors?: ValidationError[];
45
36
  requestId?: string;
46
37
  }
47
- /**
48
- * يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
49
- * @template T نوع البيانات المتوقعة في حالة النجاح.
50
- */
51
- interface StandardResponse<T> {
52
- response?: ApiResponse<T> | T;
53
- error: ApiError | null;
54
- loading: boolean;
55
- success: boolean;
56
- message?: string;
57
- validationErrors?: ValidationError[];
58
- }
59
38
  /**
60
39
  * يمثل مجموعة التوكنات التي يتم إدارتها.
61
40
  */
@@ -83,19 +62,6 @@ interface RequestConfig extends AxiosRequestConfig {
83
62
  onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
84
63
  onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
85
64
  }
86
- /**
87
- * الواجهة الرئيسية لتهيئة عميل الـ API.
88
- */
89
- interface ApiClientConfig {
90
- baseURL?: string;
91
- tokenManager: TokenManager;
92
- timeout?: number;
93
- headers?: Record<string, string>;
94
- withCredentials?: boolean;
95
- responseType?: ResponseType;
96
- pathRefreshToken?: string;
97
- onRefreshError?: (error: any) => void;
98
- }
99
65
  /**
100
66
  * يمثل خيارات الاستعلام للترقيم والبحث والفرز.
101
67
  */
@@ -122,6 +88,92 @@ interface UseApiConfig<T> {
122
88
  onSuccess?: (message: string, data?: T) => void;
123
89
  onError?: (message: string, error?: ApiError) => void;
124
90
  }
91
+ /**
92
+ * واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
93
+ */
94
+ interface RefreshTokenConfig {
95
+ /**
96
+ * المسار الذي يتم استخدامه لتجديد التوكن (e.g., '/auth/refresh').
97
+ * @default '/auth/refresh'
98
+ */
99
+ path: string;
100
+ /**
101
+ * دالة لتحديد كيفية بناء جسم (body) طلب التجديد.
102
+ * @param refreshToken - التوكن المستخدم للتجديد.
103
+ * @returns كائن يمثل جسم الطلب.
104
+ * @default (refreshToken) => ({ refresh_token: refreshToken })
105
+ */
106
+ buildRequestBody?: (refreshToken: string) => Record<string, any>;
107
+ /**
108
+ * دالة لتحديد كيفية بناء الهيدرز (headers) الخاصة بطلب التجديد.
109
+ * @param currentTokens - التوكنات الحالية.
110
+ * @returns كائن يمثل الهيدرز الإضافية.
111
+ * @default () => ({})
112
+ */
113
+ buildRequestHeaders?: (currentTokens: Tokens) => Record<string, string>;
114
+ /**
115
+ * دالة مخصصة لاستخلاص التوكنات الجديدة من استجابة الـ API.
116
+ * @param responseData - البيانات التي تم إرجاعها من الـ API.
117
+ * @returns كائن من نوع Tokens.
118
+ * @default (data) => ({ accessToken: data.access_token, ... })
119
+ */
120
+ extractTokens: (responseData: any) => {
121
+ accessToken: string;
122
+ refreshToken?: string;
123
+ expiresIn: number;
124
+ tokenType?: string;
125
+ };
126
+ }
127
+ /**
128
+ * الواجهة الرئيسية لتهيئة عميل الـ API.
129
+ */
130
+ interface ApiClientConfig {
131
+ baseURL?: string;
132
+ tokenManager: TokenManager;
133
+ timeout?: number;
134
+ headers?: Record<string, string>;
135
+ withCredentials?: boolean;
136
+ responseType?: ResponseType;
137
+ /**
138
+ * إعدادات مخصصة لعملية تجديد التوكن.
139
+ * إذا لم يتم توفيرها، ستتوقف محاولات التجديد التلقائي.
140
+ */
141
+ refreshTokenConfig?: RefreshTokenConfig;
142
+ onRefreshError?: (error: any) => void;
143
+ }
144
+ /**
145
+ * يمثل بنية الاستجابة القياسية من الـ API.
146
+ * @template T نوع البيانات الأساسية في الاستجابة.
147
+ */
148
+ interface ApiResponse<T = any> {
149
+ data: T;
150
+ message?: string;
151
+ meta?: PaginationMeta;
152
+ }
153
+ /**
154
+ * يمثل بنية الاستجابة القياسية المغلفة التي قد تأتي من بعض نقاط الـ API.
155
+ * @template T نوع البيانات الأساسية في الاستجابة.
156
+ */
157
+ interface ApiResponse<T = any> {
158
+ data: T;
159
+ message?: string;
160
+ meta?: PaginationMeta;
161
+ success?: boolean;
162
+ }
163
+ /**
164
+ * يمثل كائن الاستجابة الموحد والنهائي الذي يرجعه كل طلب.
165
+ * هذا هو النوع الذي ستتعامل معه دائمًا.
166
+ * @template T نوع البيانات النهائية التي تريد الوصول إليها.
167
+ */
168
+ interface StandardResponse<T> {
169
+ data: T | null;
170
+ rawResponse: any;
171
+ error: ApiError | null;
172
+ loading: boolean;
173
+ success: boolean;
174
+ message?: string;
175
+ validationErrors?: ValidationError[];
176
+ }
125
177
 
126
178
  /**
127
179
  * @file src/core/client.ts
@@ -151,6 +203,39 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
151
203
 
152
204
  declare function buildPaginateQuery(query: PaginateQueryOptions): string;
153
205
 
206
+ /**
207
+ * Defines a single custom API action.
208
+ * @template TRequest - The type of the data sent in the request body/params.
209
+ * @template TResponse - The type of the data expected in the successful API response.
210
+ */
211
+ type ApiAction<TRequest, TResponse> = (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;
212
+ /**
213
+ * A factory function to create a collection of typed, custom API actions.
214
+ *
215
+ * @template TActions - An object type where keys are action names and values are objects
216
+ * defining the endpoint, method, and types for that action.
217
+ * @param axiosInstance - The configured Axios instance from `createApiClient`.
218
+ * @param actionsConfig - An object defining the configuration for each custom action.
219
+ * @returns A fully-typed object of executable API action functions.
220
+ *
221
+ * @example
222
+ * const authActions = createApiActions(apiClient, {
223
+ * login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },
224
+ * getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }
225
+ * });
226
+ *
227
+ * // Usage:
228
+ * const result = await authActions.login({ email: '..', password: '..' });
229
+ */
230
+ declare function createApiActions<TActions extends Record<string, {
231
+ method: Method;
232
+ endpoint: string;
233
+ requestType: any;
234
+ responseType: any;
235
+ }>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
236
+ [K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
237
+ };
238
+
154
239
  declare class CacheManager {
155
240
  private cache;
156
241
  private defaultDuration;
@@ -162,12 +247,15 @@ declare class CacheManager {
162
247
  declare const cacheManager: CacheManager;
163
248
 
164
249
  /**
165
- * @file src/core/processor.ts
166
- * @description
167
- * ...
250
+ * A smart response processor that normalizes API responses.
251
+ * It intelligently unwraps nested data from standard API envelopes
252
+ * (like { success: true, data: {...} }) and provides direct access
253
+ * to the core data, while still handling errors consistently.
254
+ *
255
+ * @param responseOrError The raw Axios response or a pre-processed ApiError.
256
+ * @returns A standardized `StandardResponse` object with direct access to `.data`.
168
257
  */
169
-
170
- declare const processResponse: <T>(responseOrError: AxiosResponse<ApiResponse<T>> | ApiError) => StandardResponse<T>;
258
+ declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
171
259
 
172
260
  declare function useApi<T extends {
173
261
  id?: string | number;
@@ -195,4 +283,4 @@ declare function useApi<T extends {
195
283
  };
196
284
  };
197
285
 
198
- export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiClient, createApiServices, processResponse, useApi };
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 };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, AxiosResponse } from 'axios';
1
+ import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method, AxiosResponse } from 'axios';
2
2
  import * as react from 'react';
3
3
 
4
4
  /**
@@ -18,15 +18,6 @@ interface PaginationMeta {
18
18
  currentPage: number;
19
19
  totalPages: number;
20
20
  }
21
- /**
22
- * يمثل بنية الاستجابة القياسية من الـ API.
23
- * @template T نوع البيانات الأساسية في الاستجابة.
24
- */
25
- interface ApiResponse<T = any> {
26
- data: T;
27
- message?: string;
28
- meta?: PaginationMeta;
29
- }
30
21
  /**
31
22
  * يمثل خطأ التحقق من صحة حقل معين.
32
23
  */
@@ -44,18 +35,6 @@ interface ApiError {
44
35
  errors?: ValidationError[];
45
36
  requestId?: string;
46
37
  }
47
- /**
48
- * يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
49
- * @template T نوع البيانات المتوقعة في حالة النجاح.
50
- */
51
- interface StandardResponse<T> {
52
- response?: ApiResponse<T> | T;
53
- error: ApiError | null;
54
- loading: boolean;
55
- success: boolean;
56
- message?: string;
57
- validationErrors?: ValidationError[];
58
- }
59
38
  /**
60
39
  * يمثل مجموعة التوكنات التي يتم إدارتها.
61
40
  */
@@ -83,19 +62,6 @@ interface RequestConfig extends AxiosRequestConfig {
83
62
  onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
84
63
  onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
85
64
  }
86
- /**
87
- * الواجهة الرئيسية لتهيئة عميل الـ API.
88
- */
89
- interface ApiClientConfig {
90
- baseURL?: string;
91
- tokenManager: TokenManager;
92
- timeout?: number;
93
- headers?: Record<string, string>;
94
- withCredentials?: boolean;
95
- responseType?: ResponseType;
96
- pathRefreshToken?: string;
97
- onRefreshError?: (error: any) => void;
98
- }
99
65
  /**
100
66
  * يمثل خيارات الاستعلام للترقيم والبحث والفرز.
101
67
  */
@@ -122,6 +88,92 @@ interface UseApiConfig<T> {
122
88
  onSuccess?: (message: string, data?: T) => void;
123
89
  onError?: (message: string, error?: ApiError) => void;
124
90
  }
91
+ /**
92
+ * واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
93
+ */
94
+ interface RefreshTokenConfig {
95
+ /**
96
+ * المسار الذي يتم استخدامه لتجديد التوكن (e.g., '/auth/refresh').
97
+ * @default '/auth/refresh'
98
+ */
99
+ path: string;
100
+ /**
101
+ * دالة لتحديد كيفية بناء جسم (body) طلب التجديد.
102
+ * @param refreshToken - التوكن المستخدم للتجديد.
103
+ * @returns كائن يمثل جسم الطلب.
104
+ * @default (refreshToken) => ({ refresh_token: refreshToken })
105
+ */
106
+ buildRequestBody?: (refreshToken: string) => Record<string, any>;
107
+ /**
108
+ * دالة لتحديد كيفية بناء الهيدرز (headers) الخاصة بطلب التجديد.
109
+ * @param currentTokens - التوكنات الحالية.
110
+ * @returns كائن يمثل الهيدرز الإضافية.
111
+ * @default () => ({})
112
+ */
113
+ buildRequestHeaders?: (currentTokens: Tokens) => Record<string, string>;
114
+ /**
115
+ * دالة مخصصة لاستخلاص التوكنات الجديدة من استجابة الـ API.
116
+ * @param responseData - البيانات التي تم إرجاعها من الـ API.
117
+ * @returns كائن من نوع Tokens.
118
+ * @default (data) => ({ accessToken: data.access_token, ... })
119
+ */
120
+ extractTokens: (responseData: any) => {
121
+ accessToken: string;
122
+ refreshToken?: string;
123
+ expiresIn: number;
124
+ tokenType?: string;
125
+ };
126
+ }
127
+ /**
128
+ * الواجهة الرئيسية لتهيئة عميل الـ API.
129
+ */
130
+ interface ApiClientConfig {
131
+ baseURL?: string;
132
+ tokenManager: TokenManager;
133
+ timeout?: number;
134
+ headers?: Record<string, string>;
135
+ withCredentials?: boolean;
136
+ responseType?: ResponseType;
137
+ /**
138
+ * إعدادات مخصصة لعملية تجديد التوكن.
139
+ * إذا لم يتم توفيرها، ستتوقف محاولات التجديد التلقائي.
140
+ */
141
+ refreshTokenConfig?: RefreshTokenConfig;
142
+ onRefreshError?: (error: any) => void;
143
+ }
144
+ /**
145
+ * يمثل بنية الاستجابة القياسية من الـ API.
146
+ * @template T نوع البيانات الأساسية في الاستجابة.
147
+ */
148
+ interface ApiResponse<T = any> {
149
+ data: T;
150
+ message?: string;
151
+ meta?: PaginationMeta;
152
+ }
153
+ /**
154
+ * يمثل بنية الاستجابة القياسية المغلفة التي قد تأتي من بعض نقاط الـ API.
155
+ * @template T نوع البيانات الأساسية في الاستجابة.
156
+ */
157
+ interface ApiResponse<T = any> {
158
+ data: T;
159
+ message?: string;
160
+ meta?: PaginationMeta;
161
+ success?: boolean;
162
+ }
163
+ /**
164
+ * يمثل كائن الاستجابة الموحد والنهائي الذي يرجعه كل طلب.
165
+ * هذا هو النوع الذي ستتعامل معه دائمًا.
166
+ * @template T نوع البيانات النهائية التي تريد الوصول إليها.
167
+ */
168
+ interface StandardResponse<T> {
169
+ data: T | null;
170
+ rawResponse: any;
171
+ error: ApiError | null;
172
+ loading: boolean;
173
+ success: boolean;
174
+ message?: string;
175
+ validationErrors?: ValidationError[];
176
+ }
125
177
 
126
178
  /**
127
179
  * @file src/core/client.ts
@@ -151,6 +203,39 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
151
203
 
152
204
  declare function buildPaginateQuery(query: PaginateQueryOptions): string;
153
205
 
206
+ /**
207
+ * Defines a single custom API action.
208
+ * @template TRequest - The type of the data sent in the request body/params.
209
+ * @template TResponse - The type of the data expected in the successful API response.
210
+ */
211
+ type ApiAction<TRequest, TResponse> = (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;
212
+ /**
213
+ * A factory function to create a collection of typed, custom API actions.
214
+ *
215
+ * @template TActions - An object type where keys are action names and values are objects
216
+ * defining the endpoint, method, and types for that action.
217
+ * @param axiosInstance - The configured Axios instance from `createApiClient`.
218
+ * @param actionsConfig - An object defining the configuration for each custom action.
219
+ * @returns A fully-typed object of executable API action functions.
220
+ *
221
+ * @example
222
+ * const authActions = createApiActions(apiClient, {
223
+ * login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },
224
+ * getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }
225
+ * });
226
+ *
227
+ * // Usage:
228
+ * const result = await authActions.login({ email: '..', password: '..' });
229
+ */
230
+ declare function createApiActions<TActions extends Record<string, {
231
+ method: Method;
232
+ endpoint: string;
233
+ requestType: any;
234
+ responseType: any;
235
+ }>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
236
+ [K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
237
+ };
238
+
154
239
  declare class CacheManager {
155
240
  private cache;
156
241
  private defaultDuration;
@@ -162,12 +247,15 @@ declare class CacheManager {
162
247
  declare const cacheManager: CacheManager;
163
248
 
164
249
  /**
165
- * @file src/core/processor.ts
166
- * @description
167
- * ...
250
+ * A smart response processor that normalizes API responses.
251
+ * It intelligently unwraps nested data from standard API envelopes
252
+ * (like { success: true, data: {...} }) and provides direct access
253
+ * to the core data, while still handling errors consistently.
254
+ *
255
+ * @param responseOrError The raw Axios response or a pre-processed ApiError.
256
+ * @returns A standardized `StandardResponse` object with direct access to `.data`.
168
257
  */
169
-
170
- declare const processResponse: <T>(responseOrError: AxiosResponse<ApiResponse<T>> | ApiError) => StandardResponse<T>;
258
+ declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
171
259
 
172
260
  declare function useApi<T extends {
173
261
  id?: string | number;
@@ -195,4 +283,4 @@ declare function useApi<T extends {
195
283
  };
196
284
  };
197
285
 
198
- export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiClient, createApiServices, processResponse, useApi };
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 };
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  buildPaginateQuery: () => buildPaginateQuery,
34
34
  cacheManager: () => cacheManager,
35
+ createApiActions: () => createApiActions,
35
36
  createApiClient: () => createApiClient,
36
37
  createApiServices: () => createApiServices,
37
38
  processResponse: () => processResponse,
@@ -43,26 +44,44 @@ module.exports = __toCommonJS(index_exports);
43
44
  var import_axios = __toESM(require("axios"));
44
45
  var import_uuid = require("uuid");
45
46
  async function refreshToken(config, tokenManager) {
47
+ const { refreshTokenConfig } = config;
48
+ if (!refreshTokenConfig) {
49
+ console.warn("[API Core] Refresh token process is disabled (refreshTokenConfig is not provided).");
50
+ await tokenManager.clearTokens();
51
+ return null;
52
+ }
46
53
  try {
47
54
  const currentTokens = await tokenManager.getTokens();
48
- if (!currentTokens.refreshToken) throw new Error("No refresh token available.");
55
+ if (!currentTokens.refreshToken) {
56
+ throw new Error("No refresh token available to perform refresh.");
57
+ }
58
+ const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;
59
+ const requestBody = buildRequestBody ? buildRequestBody(currentTokens.refreshToken) : { refresh_token: currentTokens.refreshToken };
60
+ const requestHeaders = buildRequestHeaders ? buildRequestHeaders(currentTokens) : {};
49
61
  const response = await import_axios.default.post(
50
- `${config.baseURL}${config.pathRefreshToken || "/auth/refresh"}`,
51
- { refresh_token: currentTokens.refreshToken },
52
- { withCredentials: config.withCredentials }
62
+ `${config.baseURL}${path}`,
63
+ requestBody,
64
+ {
65
+ headers: requestHeaders,
66
+ withCredentials: config.withCredentials
67
+ }
53
68
  );
54
- const { access_token, refresh_token, expires_in, token_type } = response.data;
69
+ const extracted = extractTokens(response.data);
70
+ if (!extracted || !extracted.accessToken || typeof extracted.expiresIn !== "number") {
71
+ throw new Error("extractTokens function failed to return a valid token structure.");
72
+ }
55
73
  const newTokens = {
56
- accessToken: access_token,
57
- refreshToken: refresh_token || currentTokens.refreshToken,
58
- expiresAt: Date.now() + expires_in * 1e3,
59
- tokenType: token_type || "Bearer"
74
+ accessToken: extracted.accessToken,
75
+ refreshToken: extracted.refreshToken || currentTokens.refreshToken,
76
+ // احتفظ بالقديم إذا لم يأتِ جديد
77
+ expiresAt: Date.now() + extracted.expiresIn * 1e3,
78
+ tokenType: extracted.tokenType || "Bearer"
60
79
  };
61
80
  await tokenManager.setTokens(newTokens);
62
81
  console.log("[API Core] Tokens refreshed successfully.");
63
82
  return newTokens;
64
83
  } catch (err) {
65
- console.error("[API Core] Failed to refresh token.", err);
84
+ console.error("[API Core] Failed to refresh token.", err.response?.data || err.message);
66
85
  if (config.onRefreshError) {
67
86
  config.onRefreshError(err);
68
87
  }
@@ -95,7 +114,7 @@ function createApiClient(config) {
95
114
  const now = Date.now();
96
115
  const tokenBuffer = 60 * 1e3;
97
116
  if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
98
- if (!tokenRefreshPromise) {
117
+ if (!tokenRefreshPromise && config.refreshTokenConfig) {
99
118
  console.log("[API Core] Proactive token refresh initiated.");
100
119
  tokenRefreshPromise = refreshToken(config, tokenManager);
101
120
  }
@@ -115,7 +134,7 @@ function createApiClient(config) {
115
134
  const originalRequest = error.config;
116
135
  if (error.response?.status === 401 && !originalRequest._retry) {
117
136
  originalRequest._retry = true;
118
- if (!tokenRefreshPromise) {
137
+ if (!tokenRefreshPromise && config.refreshTokenConfig) {
119
138
  console.log("[API Core] Reactive token refresh initiated due to 401.");
120
139
  tokenRefreshPromise = refreshToken(config, tokenManager);
121
140
  }
@@ -170,37 +189,38 @@ function isAxiosResponse(obj) {
170
189
  var processResponse = (responseOrError) => {
171
190
  if (isApiError(responseOrError)) {
172
191
  return {
173
- response: void 0,
192
+ data: null,
193
+ rawResponse: void 0,
174
194
  error: responseOrError,
175
195
  validationErrors: responseOrError.errors || [],
176
- // الخطأ الآن صحيح
177
196
  success: false,
178
197
  loading: false,
179
198
  message: responseOrError.message
180
199
  };
181
200
  }
182
201
  if (isAxiosResponse(responseOrError)) {
183
- const response = responseOrError;
202
+ const rawData = responseOrError.data;
203
+ const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
204
+ const finalData = isWrappedResponse ? rawData.data : rawData;
205
+ const message = isWrappedResponse ? rawData.message : "Request successful.";
184
206
  return {
185
- response: response.data,
207
+ data: finalData,
208
+ // <-- وصول مباشر للبيانات النهائية!
209
+ rawResponse: rawData,
210
+ // احتفظ بالاستجابة الكاملة إذا احتجت إليها
186
211
  loading: false,
187
212
  success: true,
188
213
  error: null,
189
- message: response.data.message || "Request successful.",
214
+ message,
190
215
  validationErrors: []
191
- // <-- أضف قيمة افتراضية هنا للاتساق
192
216
  };
193
217
  }
194
218
  return {
195
- response: void 0,
196
- error: {
197
- message: "An unknown error occurred during response processing.",
198
- status: 500
199
- },
219
+ data: null,
220
+ rawResponse: void 0,
221
+ error: { message: "An unknown error occurred during response processing.", status: 500 },
200
222
  success: false,
201
- loading: false,
202
- validationErrors: []
203
- // <-- أضف قيمة افتراضية هنا أيضًا
223
+ loading: false
204
224
  };
205
225
  };
206
226
 
@@ -250,6 +270,34 @@ function createApiServices(axiosInstance, endpoint) {
250
270
  return { get, getWithQuery, post, patch, remove };
251
271
  }
252
272
 
273
+ // src/services/actions.ts
274
+ function createAction(axiosInstance, method, endpoint) {
275
+ return async (payload, config) => {
276
+ try {
277
+ const response = await axiosInstance.request({
278
+ // <--- غيرنا النوع إلى any
279
+ url: endpoint,
280
+ method,
281
+ ...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
282
+ ...config
283
+ });
284
+ return processResponse(response);
285
+ } catch (error) {
286
+ return processResponse(error);
287
+ }
288
+ };
289
+ }
290
+ function createApiActions(axiosInstance, actionsConfig) {
291
+ const actions = {};
292
+ for (const actionName in actionsConfig) {
293
+ if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
294
+ const { method, endpoint } = actionsConfig[actionName];
295
+ actions[actionName] = createAction(axiosInstance, method, endpoint);
296
+ }
297
+ }
298
+ return actions;
299
+ }
300
+
253
301
  // src/core/cache.ts
254
302
  var CacheManager = class {
255
303
  cache = /* @__PURE__ */ new Map();
@@ -294,7 +342,9 @@ function useApi(axiosInstance, config) {
294
342
  onError
295
343
  } = config;
296
344
  const [state, setState] = (0, import_react.useState)({
297
- response: initialData || void 0,
345
+ data: initialData || null,
346
+ // <--- التغيير هنا
347
+ rawResponse: null,
298
348
  loading: enabled,
299
349
  error: null,
300
350
  success: false
@@ -302,7 +352,7 @@ function useApi(axiosInstance, config) {
302
352
  const [queryOptions, setQueryOptions] = (0, import_react.useState)(initialQuery);
303
353
  const apiServices = (0, import_react.useRef)(createApiServices(axiosInstance, endpoint)).current;
304
354
  const fetchData = (0, import_react.useCallback)(async () => {
305
- setState((prev) => ({ ...prev, loading: true, error: null }));
355
+ setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
306
356
  const queryString = buildPaginateQuery(queryOptions);
307
357
  const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
308
358
  setState(result);
@@ -321,7 +371,7 @@ function useApi(axiosInstance, config) {
321
371
  if (result.success) {
322
372
  if (refetchAfterChange) await fetchData();
323
373
  else setState((prev) => ({ ...prev, loading: false }));
324
- if (onSuccess) onSuccess(result.message || "Item created successfully!", result.response);
374
+ if (onSuccess) onSuccess(result.message || "Item created successfully!", result.data);
325
375
  } else {
326
376
  setState((prev) => ({ ...prev, loading: false, error: result.error }));
327
377
  if (onError) onError(result.message || "Create failed", result.error || void 0);
@@ -334,7 +384,7 @@ function useApi(axiosInstance, config) {
334
384
  if (result.success) {
335
385
  if (refetchAfterChange) await fetchData();
336
386
  else setState((prev) => ({ ...prev, loading: false }));
337
- if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.response);
387
+ if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.data);
338
388
  } else {
339
389
  setState((prev) => ({ ...prev, loading: false, error: result.error }));
340
390
  if (onError) onError(result.message || "Update failed", result.error || void 0);
@@ -386,6 +436,7 @@ function useApi(axiosInstance, config) {
386
436
  0 && (module.exports = {
387
437
  buildPaginateQuery,
388
438
  cacheManager,
439
+ createApiActions,
389
440
  createApiClient,
390
441
  createApiServices,
391
442
  processResponse,
package/dist/index.mjs CHANGED
@@ -2,26 +2,44 @@
2
2
  import axios from "axios";
3
3
  import { v4 as uuidv4 } from "uuid";
4
4
  async function refreshToken(config, tokenManager) {
5
+ const { refreshTokenConfig } = config;
6
+ if (!refreshTokenConfig) {
7
+ console.warn("[API Core] Refresh token process is disabled (refreshTokenConfig is not provided).");
8
+ await tokenManager.clearTokens();
9
+ return null;
10
+ }
5
11
  try {
6
12
  const currentTokens = await tokenManager.getTokens();
7
- if (!currentTokens.refreshToken) throw new Error("No refresh token available.");
13
+ if (!currentTokens.refreshToken) {
14
+ throw new Error("No refresh token available to perform refresh.");
15
+ }
16
+ const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;
17
+ const requestBody = buildRequestBody ? buildRequestBody(currentTokens.refreshToken) : { refresh_token: currentTokens.refreshToken };
18
+ const requestHeaders = buildRequestHeaders ? buildRequestHeaders(currentTokens) : {};
8
19
  const response = await axios.post(
9
- `${config.baseURL}${config.pathRefreshToken || "/auth/refresh"}`,
10
- { refresh_token: currentTokens.refreshToken },
11
- { withCredentials: config.withCredentials }
20
+ `${config.baseURL}${path}`,
21
+ requestBody,
22
+ {
23
+ headers: requestHeaders,
24
+ withCredentials: config.withCredentials
25
+ }
12
26
  );
13
- const { access_token, refresh_token, expires_in, token_type } = response.data;
27
+ const extracted = extractTokens(response.data);
28
+ if (!extracted || !extracted.accessToken || typeof extracted.expiresIn !== "number") {
29
+ throw new Error("extractTokens function failed to return a valid token structure.");
30
+ }
14
31
  const newTokens = {
15
- accessToken: access_token,
16
- refreshToken: refresh_token || currentTokens.refreshToken,
17
- expiresAt: Date.now() + expires_in * 1e3,
18
- tokenType: token_type || "Bearer"
32
+ accessToken: extracted.accessToken,
33
+ refreshToken: extracted.refreshToken || currentTokens.refreshToken,
34
+ // احتفظ بالقديم إذا لم يأتِ جديد
35
+ expiresAt: Date.now() + extracted.expiresIn * 1e3,
36
+ tokenType: extracted.tokenType || "Bearer"
19
37
  };
20
38
  await tokenManager.setTokens(newTokens);
21
39
  console.log("[API Core] Tokens refreshed successfully.");
22
40
  return newTokens;
23
41
  } catch (err) {
24
- console.error("[API Core] Failed to refresh token.", err);
42
+ console.error("[API Core] Failed to refresh token.", err.response?.data || err.message);
25
43
  if (config.onRefreshError) {
26
44
  config.onRefreshError(err);
27
45
  }
@@ -54,7 +72,7 @@ function createApiClient(config) {
54
72
  const now = Date.now();
55
73
  const tokenBuffer = 60 * 1e3;
56
74
  if (tokens.accessToken && tokens.expiresAt && tokens.expiresAt - now < tokenBuffer) {
57
- if (!tokenRefreshPromise) {
75
+ if (!tokenRefreshPromise && config.refreshTokenConfig) {
58
76
  console.log("[API Core] Proactive token refresh initiated.");
59
77
  tokenRefreshPromise = refreshToken(config, tokenManager);
60
78
  }
@@ -74,7 +92,7 @@ function createApiClient(config) {
74
92
  const originalRequest = error.config;
75
93
  if (error.response?.status === 401 && !originalRequest._retry) {
76
94
  originalRequest._retry = true;
77
- if (!tokenRefreshPromise) {
95
+ if (!tokenRefreshPromise && config.refreshTokenConfig) {
78
96
  console.log("[API Core] Reactive token refresh initiated due to 401.");
79
97
  tokenRefreshPromise = refreshToken(config, tokenManager);
80
98
  }
@@ -129,37 +147,38 @@ function isAxiosResponse(obj) {
129
147
  var processResponse = (responseOrError) => {
130
148
  if (isApiError(responseOrError)) {
131
149
  return {
132
- response: void 0,
150
+ data: null,
151
+ rawResponse: void 0,
133
152
  error: responseOrError,
134
153
  validationErrors: responseOrError.errors || [],
135
- // الخطأ الآن صحيح
136
154
  success: false,
137
155
  loading: false,
138
156
  message: responseOrError.message
139
157
  };
140
158
  }
141
159
  if (isAxiosResponse(responseOrError)) {
142
- const response = responseOrError;
160
+ const rawData = responseOrError.data;
161
+ const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
162
+ const finalData = isWrappedResponse ? rawData.data : rawData;
163
+ const message = isWrappedResponse ? rawData.message : "Request successful.";
143
164
  return {
144
- response: response.data,
165
+ data: finalData,
166
+ // <-- وصول مباشر للبيانات النهائية!
167
+ rawResponse: rawData,
168
+ // احتفظ بالاستجابة الكاملة إذا احتجت إليها
145
169
  loading: false,
146
170
  success: true,
147
171
  error: null,
148
- message: response.data.message || "Request successful.",
172
+ message,
149
173
  validationErrors: []
150
- // <-- أضف قيمة افتراضية هنا للاتساق
151
174
  };
152
175
  }
153
176
  return {
154
- response: void 0,
155
- error: {
156
- message: "An unknown error occurred during response processing.",
157
- status: 500
158
- },
177
+ data: null,
178
+ rawResponse: void 0,
179
+ error: { message: "An unknown error occurred during response processing.", status: 500 },
159
180
  success: false,
160
- loading: false,
161
- validationErrors: []
162
- // <-- أضف قيمة افتراضية هنا أيضًا
181
+ loading: false
163
182
  };
164
183
  };
165
184
 
@@ -209,6 +228,34 @@ function createApiServices(axiosInstance, endpoint) {
209
228
  return { get, getWithQuery, post, patch, remove };
210
229
  }
211
230
 
231
+ // src/services/actions.ts
232
+ function createAction(axiosInstance, method, endpoint) {
233
+ return async (payload, config) => {
234
+ try {
235
+ const response = await axiosInstance.request({
236
+ // <--- غيرنا النوع إلى any
237
+ url: endpoint,
238
+ method,
239
+ ...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
240
+ ...config
241
+ });
242
+ return processResponse(response);
243
+ } catch (error) {
244
+ return processResponse(error);
245
+ }
246
+ };
247
+ }
248
+ function createApiActions(axiosInstance, actionsConfig) {
249
+ const actions = {};
250
+ for (const actionName in actionsConfig) {
251
+ if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
252
+ const { method, endpoint } = actionsConfig[actionName];
253
+ actions[actionName] = createAction(axiosInstance, method, endpoint);
254
+ }
255
+ }
256
+ return actions;
257
+ }
258
+
212
259
  // src/core/cache.ts
213
260
  var CacheManager = class {
214
261
  cache = /* @__PURE__ */ new Map();
@@ -253,7 +300,9 @@ function useApi(axiosInstance, config) {
253
300
  onError
254
301
  } = config;
255
302
  const [state, setState] = useState({
256
- response: initialData || void 0,
303
+ data: initialData || null,
304
+ // <--- التغيير هنا
305
+ rawResponse: null,
257
306
  loading: enabled,
258
307
  error: null,
259
308
  success: false
@@ -261,7 +310,7 @@ function useApi(axiosInstance, config) {
261
310
  const [queryOptions, setQueryOptions] = useState(initialQuery);
262
311
  const apiServices = useRef(createApiServices(axiosInstance, endpoint)).current;
263
312
  const fetchData = useCallback(async () => {
264
- setState((prev) => ({ ...prev, loading: true, error: null }));
313
+ setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
265
314
  const queryString = buildPaginateQuery(queryOptions);
266
315
  const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
267
316
  setState(result);
@@ -280,7 +329,7 @@ function useApi(axiosInstance, config) {
280
329
  if (result.success) {
281
330
  if (refetchAfterChange) await fetchData();
282
331
  else setState((prev) => ({ ...prev, loading: false }));
283
- if (onSuccess) onSuccess(result.message || "Item created successfully!", result.response);
332
+ if (onSuccess) onSuccess(result.message || "Item created successfully!", result.data);
284
333
  } else {
285
334
  setState((prev) => ({ ...prev, loading: false, error: result.error }));
286
335
  if (onError) onError(result.message || "Create failed", result.error || void 0);
@@ -293,7 +342,7 @@ function useApi(axiosInstance, config) {
293
342
  if (result.success) {
294
343
  if (refetchAfterChange) await fetchData();
295
344
  else setState((prev) => ({ ...prev, loading: false }));
296
- if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.response);
345
+ if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.data);
297
346
  } else {
298
347
  setState((prev) => ({ ...prev, loading: false, error: result.error }));
299
348
  if (onError) onError(result.message || "Update failed", result.error || void 0);
@@ -344,6 +393,7 @@ function useApi(axiosInstance, config) {
344
393
  export {
345
394
  buildPaginateQuery,
346
395
  cacheManager,
396
+ createApiActions,
347
397
  createApiClient,
348
398
  createApiServices,
349
399
  processResponse,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "2.2.2",
3
+ "version": "3.3.2",
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",