api-core-lib 12.0.5 → 12.0.6
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.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -1
- package/dist/server.d.ts +11 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/package.json +21 -27
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/client.ts","../src/core/processor.ts","../src/core/utils.ts","../src/core/buildDynamicUrl.ts","../src/services/crud.ts","../src/services/actions.ts","../src/core/globalStateManager.ts","../src/core/cache.ts","../src/hooks/useApi.ts","../src/hooks/useApiRecord/index.ts","../src/hooks/useDeepCompareEffect/index.ts","../src/hooks/useApiModule/useApiModule.ts"],"sourcesContent":["// file: api-core-lib/src/index.ts\r\n\r\n/**\r\n * ===================================================================================\r\n * CLIENT-SIDE ENTRY POINT\r\n * ===================================================================================\r\n * This file exports all utilities, hooks, and types needed for a client-side\r\n * React application (components marked with \"use client\").\r\n * It MUST NOT export server-only utilities.\r\n */\r\n\r\n// --- Core utilities safe for both environments ---\r\nexport { createApiClient } from './core/client';\r\nexport { callDynamicApi, createApiServices } from './services/crud';\r\nexport { createApiActions } from './services/actions';\r\nexport { globalStateManager } from './core/globalStateManager';\r\nexport { buildPaginateQuery } from './core/utils';\r\nexport { processResponse } from './core/processor';\r\n\r\n// [NOTE] Consolidating state managers. \r\n// It's recommended to use 'globalStateManager' from the new architecture.\r\n// 'cacheManager' is exported for legacy compatibility if needed.\r\nexport { cacheManager } from './core/cache';\r\n\r\n// --- Hooks (Client components ONLY) ---\r\nexport { useApi } from './hooks/useApi';\r\nexport { useApiRecord } from './hooks/useApiRecord';\r\nexport { useDeepCompareEffect } from './hooks/useDeepCompareEffect';\r\n\r\n// The primary hook for the new architecture\r\nexport { useApiModule, useModuleContext, ApiModuleProvider } from './hooks/useApiModule/useApiModule';\r\n\r\n// --- Types (safe to export everywhere) ---\r\nexport * from './types';\r\nexport * from './types/apiModule.types';\r\nexport * from './types/useApi.types';\r\nexport * from './types/useApiResource.types';\r\nexport * from './types/useApiRecord.types';","import axios, {\r\n AxiosInstance,\r\n AxiosError,\r\n AxiosResponse,\r\n InternalAxiosRequestConfig,\r\n} from 'axios';\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport {\r\n ApiClientConfig,\r\n Middleware,\r\n MiddlewareContext,\r\n ApiError,\r\n RequestConfig,\r\n Tokens,\r\n TokenManager,\r\n} from '../types';\r\n\r\n// ملاحظة هامة: يجب عليك تحديث تعريف `MiddlewareContext` في ملف `types.ts`\r\n// وحذف خاصية `logger` منه.\r\n\r\nasync function runMiddleware(\r\n context: MiddlewareContext,\r\n middlewares: Middleware[] = [],\r\n): Promise<void> {\r\n const run = async (index: number): Promise<void> => {\r\n if (index >= middlewares.length) return;\r\n const middleware = middlewares[index];\r\n await middleware(context, () => run(index + 1));\r\n };\r\n await run(0);\r\n}\r\n\r\n// تم حذف معامل `logger` من الدالة\r\nasync function refreshToken(\r\n config: ApiClientConfig,\r\n tokenManager: TokenManager,\r\n): Promise<Tokens | null> {\r\n const { refreshTokenConfig } = config;\r\n\r\n if (!refreshTokenConfig) {\r\n console.warn('Token refresh is disabled: `refreshTokenConfig` is not provided.');\r\n await tokenManager.clearTokens();\r\n return null;\r\n }\r\n\r\n try {\r\n const currentTokens = await tokenManager.getTokens();\r\n if (!currentTokens.refreshToken) {\r\n throw new Error(\"No refresh token available to perform refresh.\");\r\n }\r\n\r\n const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;\r\n \r\n const requestBody = buildRequestBody \r\n ? buildRequestBody(currentTokens.refreshToken) \r\n : { refresh_token: currentTokens.refreshToken }; \r\n\r\n const requestHeaders = buildRequestHeaders \r\n ? buildRequestHeaders(currentTokens) \r\n : {};\r\n \r\n const response = await axios.post(\r\n `${config.baseURL}${path}`, \r\n requestBody,\r\n { headers: requestHeaders, withCredentials: config.withCredentials }\r\n );\r\n\r\n const extracted = extractTokens(response.data);\r\n if (!extracted || !extracted.accessToken || typeof extracted.expiresIn !== 'number') {\r\n throw new Error(\"extractTokens function failed to return a valid token structure.\");\r\n }\r\n \r\n const newTokens: Tokens = {\r\n accessToken: extracted.accessToken,\r\n refreshToken: extracted.refreshToken || currentTokens.refreshToken,\r\n expiresAt: Date.now() + extracted.expiresIn * 1000,\r\n tokenType: extracted.tokenType || 'Bearer',\r\n };\r\n\r\n await tokenManager.setTokens(newTokens);\r\n console.info('Tokens refreshed successfully.');\r\n return newTokens;\r\n\r\n } catch (err: any) {\r\n console.error('Failed to refresh token.', err.response?.data || err.message);\r\n if (config.onRefreshError) {\r\n config.onRefreshError(err);\r\n }\r\n await tokenManager.clearTokens();\r\n return null;\r\n }\r\n}\r\n\r\nexport function createApiClient(config: ApiClientConfig): AxiosInstance {\r\n const {\r\n baseURL,\r\n tokenManager,\r\n timeout = 15000,\r\n headers = {},\r\n withCredentials = false,\r\n middleware = [],\r\n defaultIsPublic = false,\r\n maxTokenRefreshRetries = 2,\r\n maxQueueSize = 50,\r\n onRefreshError\r\n } = config;\r\n \r\n // لا يوجد متغير `logger`، سيتم استخدام `console` مباشرة\r\n\r\n const axiosInstance = axios.create({\r\n baseURL,\r\n timeout,\r\n headers: { 'Content-Type': 'application/json', ...headers },\r\n withCredentials,\r\n });\r\n\r\n let isRefreshing = false;\r\n let refreshAttempts = 0;\r\n let failedQueue: { resolve: (value: any) => void; reject: (reason?: any) => void }[] = [];\r\n\r\n const processQueue = (error: any, token: string | null = null) => {\r\n failedQueue.forEach(prom => (error ? prom.reject(error) : prom.resolve(token)));\r\n failedQueue = [];\r\n };\r\n\r\n const safeRunMiddleware = async (context: MiddlewareContext, middlewareList?: Middleware[]) => {\r\n try {\r\n await runMiddleware(context, middlewareList);\r\n } catch (err) {\r\n console.error('Middleware Error', err);\r\n }\r\n };\r\n\r\n axiosInstance.interceptors.request.use(async (req: InternalAxiosRequestConfig & RequestConfig) => {\r\n req.headers['X-Request-ID'] = uuidv4();\r\n try {\r\n const fullUrl = axios.getUri({ ...req, baseURL: req.baseURL ?? baseURL });\r\n console.log(`Request Sent > ${req.method?.toUpperCase()} ${fullUrl}`, `(ID: ${req.headers['X-Request-ID']})`);\r\n } catch {\r\n console.warn('Failed to build full request URL for logging.');\r\n }\r\n\r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req, custom: {} }, middleware);\r\n\r\n if (req.isPublic ?? defaultIsPublic) {\r\n return req;\r\n }\r\n\r\n if (tokenManager.isHttpOnly()) return req;\r\n\r\n let tokens = await tokenManager.getTokens();\r\n const now = Date.now();\r\n const tokenBuffer = 60 * 1000;\r\n\r\n if (tokens.accessToken && tokens.expiresAt && (tokens.expiresAt - now < tokenBuffer) && !isRefreshing) {\r\n if (config.refreshTokenConfig) {\r\n console.info('Proactive token refresh initiated.');\r\n isRefreshing = true;\r\n try {\r\n const newTokens = await refreshToken(config, tokenManager);\r\n if (newTokens) tokens = newTokens;\r\n } catch (err) {\r\n console.error('Proactive refresh failed', err);\r\n } finally {\r\n isRefreshing = false;\r\n }\r\n }\r\n }\r\n\r\n if (tokens.accessToken) {\r\n req.headers.Authorization = `${tokens.tokenType || 'Bearer'} ${tokens.accessToken}`;\r\n }\r\n\r\n return req;\r\n });\r\n\r\n axiosInstance.interceptors.response.use(\r\n async (res) => {\r\n try {\r\n const fullUrl = axios.getUri(res.config);\r\n console.log(`Response Received < ${res.config.method?.toUpperCase()} ${fullUrl}`, `(ID: ${res.config.headers['X-Request-ID']}, Status: ${res.status})`);\r\n } catch {\r\n console.warn('Failed to build full response URL for logging.');\r\n }\r\n \r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req: res.config, res, custom: {} }, middleware);\r\n return res;\r\n },\r\n async (error: AxiosError) => {\r\n const originalRequest = error.config as any;\r\n\r\n // [الحل الرئيسي] إعادة بناء منطق تسجيل الأخطاء بالكامل\r\n try {\r\n const fullUrl = axios.getUri(originalRequest);\r\n const method = originalRequest.method?.toUpperCase();\r\n const status = error.response?.status;\r\n const responseData = error.response?.data as { message?: string } | undefined;\r\n\r\n // 1. بناء رسالة خطأ موجزة ومفيدة\r\n const summary = `Response Error < ${method} ${fullUrl} | Status: ${status || 'N/A'}`;\r\n \r\n // 2. استخراج رسالة الخطأ من ה-API إن وجدت\r\n const apiMessage = responseData?.message || error.message;\r\n\r\n // 3. طباعة الرسالة الموجزة ورسالة ה-API\r\n console.error(summary);\r\n console.error(`> Message: ${apiMessage}`);\r\n\r\n // 4. [اختياري ولكن موصى به] طباعة كائن الاستجابة الكامل بشكل قابل للفحص\r\n if (error.response) {\r\n console.groupCollapsed('Full Error Response Details');\r\n console.dir(error.response.data);\r\n console.groupEnd();\r\n }\r\n \r\n } catch {\r\n console.error('Response Error: Failed to build full URL for logging.', error.message);\r\n }\r\n\r\n \r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req: originalRequest, error, custom: {} }, middleware);\r\n\r\n const isHttpOnlyMode = tokenManager.isHttpOnly();\r\n const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;\r\n\r\n if (isHttpOnlyMode && canAttemptRefresh) {\r\n console.error(\"401 in httpOnly mode. Token refresh must be handled by the server.\");\r\n return Promise.reject(error);\r\n }\r\n\r\n if (!isHttpOnlyMode && canAttemptRefresh) {\r\n if (isRefreshing) {\r\n if (failedQueue.length >= maxQueueSize) {\r\n console.warn('Failed request queue overflow, rejecting request.');\r\n return Promise.reject(error);\r\n }\r\n return new Promise((resolve, reject) => failedQueue.push({ resolve, reject }))\r\n .then(token => {\r\n originalRequest.headers['Authorization'] = `Bearer ${token}`;\r\n return axiosInstance(originalRequest);\r\n });\r\n }\r\n\r\n if (refreshAttempts >= maxTokenRefreshRetries) {\r\n console.error('Max token refresh attempts reached.');\r\n return Promise.reject(error);\r\n }\r\n\r\n refreshAttempts++;\r\n originalRequest._retry = true;\r\n isRefreshing = true;\r\n\r\n try {\r\n // استدعاء الدالة بدون `logger`\r\n const newTokens = await refreshToken(config, tokenManager);\r\n if (!newTokens?.accessToken) {\r\n throw new Error('Token refresh failed to produce a new access token.');\r\n }\r\n processQueue(null, newTokens.accessToken);\r\n originalRequest.headers['Authorization'] = `${newTokens.tokenType || 'Bearer'} ${newTokens.accessToken}`;\r\n return axiosInstance(originalRequest);\r\n } catch (refreshError) {\r\n processQueue(refreshError, null);\r\n return Promise.reject(refreshError);\r\n } finally {\r\n isRefreshing = false;\r\n }\r\n }\r\n\r\n return Promise.reject(error);\r\n }\r\n );\r\n\r\n return axiosInstance;\r\n}\r\n\r\n\r\n// v1\r\n// export function createApiClient(config: ApiClientConfig): AxiosInstance {\r\n// const {\r\n// baseURL,\r\n// tokenManager,\r\n// timeout = 15000,\r\n// headers = {},\r\n// withCredentials = false,\r\n// middleware = [],\r\n// logger: externalLogger = console,\r\n// // ✅ NEW: logLevel is now an optional parameter with a default value.\r\n// logLevel = 'info',\r\n// defaultIsPublic = false,\r\n// } = config;\r\n\r\n// // --- Logger Wrapper for Log Level Control ---\r\n// const LOG_LEVELS: Record<LogLevel, number> = { 'debug': 4, 'info': 3, 'warn': 2, 'error': 1, 'silent': 0 };\r\n// const CURRENT_LOG_LEVEL = LOG_LEVELS[logLevel] ?? LOG_LEVELS.info;\r\n\r\n// const logger: Logger = {\r\n// debug: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.debug) {\r\n// if (typeof externalLogger.debug === 'function') {\r\n// externalLogger.debug(`[DEBUG] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[DEBUG] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// info: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.info) {\r\n// // التحقق من .info (ممارسة جيدة)\r\n// if (typeof externalLogger.info === 'function') {\r\n// externalLogger.info(message, ...args);\r\n// } else {\r\n// externalLogger.log(message, ...args);\r\n// }\r\n// }\r\n// },\r\n// warn: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.warn) {\r\n// // التحقق من .warn (ممارسة جيدة)\r\n// if (typeof externalLogger.warn === 'function') {\r\n// externalLogger.warn(`[WARN] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[WARN] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// error: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.error) {\r\n// // التحقق من .error (ممارسة جيدة)\r\n// if (typeof externalLogger.error === 'function') {\r\n// externalLogger.error(`[ERROR] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[ERROR] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// log: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.debug) {\r\n// // .log دائمًا موجود في تعريف الواجهة، لذا لا حاجة للتحقق\r\n// externalLogger.log(`[DEBUG] ${message}`, ...args);\r\n// }\r\n// },\r\n// };\r\n \r\n// const axiosInstance = axios.create({\r\n// baseURL,\r\n// timeout,\r\n// headers: { 'Content-Type': 'application/json', ...headers },\r\n// withCredentials,\r\n// });\r\n\r\n// let isRefreshing = false;\r\n// let failedQueue: { resolve: (value: any) => void; reject: (reason?: any) => void }[] = [];\r\n\r\n// const processQueue = (error: any, token: string | null = null) => {\r\n// failedQueue.forEach(prom => (error ? prom.reject(error) : prom.resolve(token)));\r\n// failedQueue = [];\r\n// };\r\n\r\n// // --- Request Interceptor ---\r\n// axiosInstance.interceptors.request.use(async (req: InternalAxiosRequestConfig & RequestConfig) => {\r\n// req.headers['X-Request-ID'] = uuidv4();\r\n\r\n// // ✅ NEW: Construct and log the full URL.\r\n// const fullUrl = axios.getUri({ ...req, baseURL: req.baseURL ?? baseURL });\r\n// logger.info(`[Request] > ${req.method?.toUpperCase()} ${fullUrl}`, { id: req.headers['X-Request-ID'] });\r\n// logger.debug('Request Details:', { headers: req.headers, data: req.data });\r\n\r\n// const context: MiddlewareContext = { req, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n\r\n// const isRequestPublic = req.isPublic ?? defaultIsPublic;\r\n\r\n// if (isRequestPublic) {\r\n// logger.debug('[API Core] Request is public, skipping token attachment.');\r\n// return req; \r\n// }\r\n \r\n// if (tokenManager.isHttpOnly()) return req;\r\n\r\n// let tokens = await tokenManager.getTokens();\r\n// const now = Date.now();\r\n// const tokenBuffer = 60 * 1000;\r\n\r\n// if (tokens.accessToken && tokens.expiresAt && (tokens.expiresAt - now < tokenBuffer) && !isRefreshing) {\r\n// if (config.refreshTokenConfig) {\r\n// logger.info('[API Core] Proactive token refresh initiated.');\r\n// isRefreshing = true;\r\n// try {\r\n// const newTokens = await refreshToken(config, tokenManager, logger);\r\n// if (newTokens) tokens = newTokens;\r\n// } finally {\r\n// isRefreshing = false;\r\n// }\r\n// }\r\n// }\r\n\r\n// if (tokens.accessToken) {\r\n// req.headers.Authorization = `${tokens.tokenType || 'Bearer'} ${tokens.accessToken}`;\r\n// }\r\n\r\n// return req;\r\n// });\r\n\r\n// // --- Response Interceptor ---\r\n// axiosInstance.interceptors.response.use(\r\n// async (res: AxiosResponse) => {\r\n// const fullUrl = axios.getUri(res.config);\r\n// logger.info(`[Response] < ${res.config.method?.toUpperCase()} ${fullUrl}`, { id: res.config.headers['X-Request-ID'], status: res.status });\r\n// logger.debug('Response Data:', res.data);\r\n\r\n// const context: MiddlewareContext = { req: res.config, res, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n// return res;\r\n// },\r\n// async (error: AxiosError<ApiError>) => {\r\n// const originalRequest = error.config as InternalAxiosRequestConfig & RequestConfig & { _retry?: boolean };\r\n// const fullUrl = axios.getUri(originalRequest);\r\n \r\n// logger.error(`[Response Error] < ${originalRequest.method?.toUpperCase()} ${fullUrl}`, {\r\n// id: originalRequest.headers?.['X-Request-ID'],\r\n// status: error.response?.status,\r\n// message: error.message,\r\n// });\r\n\r\n// logger.debug('Full Error Object:', error.toJSON ? error.toJSON() : error);\r\n \r\n// const context: MiddlewareContext = { req: originalRequest, error, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n\r\n// // ... (rest of the token refresh logic is unchanged) ...\r\n// const isHttpOnlyMode = tokenManager.isHttpOnly();\r\n// const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;\r\n\r\n// if (isHttpOnlyMode && canAttemptRefresh) {\r\n// logger.error(\"[API Core] Fatal: Received 401 in httpOnly mode. Token refresh must be handled by the server-side proxy. The request will not be retried from the client.\");\r\n// return Promise.reject(error);\r\n// }\r\n \r\n// if (!isHttpOnlyMode && canAttemptRefresh) {\r\n// if (isRefreshing) {\r\n// return new Promise((resolve, reject) => {\r\n// failedQueue.push({ resolve, reject });\r\n// }).then(token => {\r\n// originalRequest.headers['Authorization'] = `Bearer ${token}`;\r\n// return axiosInstance(originalRequest);\r\n// });\r\n// }\r\n \r\n// originalRequest._retry = true;\r\n// isRefreshing = true;\r\n// try {\r\n// const newTokens = await refreshToken(config, tokenManager, logger);\r\n// if (!newTokens || !newTokens.accessToken) {\r\n// throw new Error('Token refresh failed to produce a new access token.');\r\n// }\r\n// processQueue(null, newTokens.accessToken);\r\n// originalRequest.headers['Authorization'] = `${newTokens.tokenType || 'Bearer'} ${newTokens.accessToken}`;\r\n// return axiosInstance(originalRequest);\r\n// } catch (refreshError) {\r\n// processQueue(refreshError, null);\r\n// return Promise.reject(refreshError);\r\n// } finally {\r\n// isRefreshing = false;\r\n// }\r\n// }\r\n\r\n// return Promise.reject(error);\r\n// }\r\n// );\r\n\r\n// return axiosInstance;\r\n// }","import axios, { AxiosError, AxiosResponse } from 'axios';\r\nimport { ApiError, StandardResponse, ValidationError } from '../types';\r\n// [مهم] استيراد دوال الحماية الجديدة\r\nimport { isServerError, isNetworkError, isAxiosResponse } from './utils';\r\n\r\n/**\r\n * [النسخة النهائية والمضمونة]\r\n * تستخدم دوال حماية النوع المخصصة للقضاء على أخطاء 'never' بشكل نهائي.\r\n */\r\nexport const processResponse = <T>(\r\n responseOrError: AxiosResponse<any> | AxiosError,\r\n): StandardResponse<T> => {\r\n \r\n // ===================================================================================\r\n // #region المسار 1: معالجة النجاح\r\n // ===================================================================================\r\n if (isAxiosResponse(responseOrError)) {\r\n const response = responseOrError;\r\n const rawData = response.data;\r\n const isStandardApiResponse = rawData && typeof rawData.success === 'boolean' && rawData.data !== undefined;\r\n \r\n return {\r\n data: isStandardApiResponse ? rawData.data : rawData,\r\n links: isStandardApiResponse ? rawData.links : undefined,\r\n meta: isStandardApiResponse ? rawData.meta : undefined,\r\n rawResponse: rawData,\r\n loading: false, success: true, error: null,\r\n message: isStandardApiResponse ? rawData.message : 'Request successful.',\r\n validationErrors: [],\r\n };\r\n }\r\n\r\n // ===================================================================================\r\n // #region المسار 2: معالجة الأخطاء\r\n // ===================================================================================\r\n\r\n // الحالة 2.1: خطأ من الخادم (4xx/5xx)\r\n // داخل هذا البلوك، TypeScript متأكد 100% أن `responseOrError.response` موجود.\r\n if (isServerError(responseOrError)) {\r\n const error = responseOrError; // الآن `error` من النوع الصحيح\r\n type ApiErrorResponse = { message?: string; code?: string; errors?: ValidationError[] };\r\n const responseData = error.response.data as ApiErrorResponse | undefined;\r\n const status = error.response.status;\r\n let defaultMessage = 'An unexpected server error occurred.';\r\n\r\n switch (status) {\r\n case 400: defaultMessage = 'Bad Request'; break;\r\n case 401: defaultMessage = 'Unauthorized'; break;\r\n case 403: defaultMessage = 'Forbidden'; break;\r\n case 404: defaultMessage = 'Not Found'; break;\r\n case 422: defaultMessage = 'Validation Failed'; break;\r\n case 500: defaultMessage = 'Internal Server Error'; break;\r\n case 503: defaultMessage = 'Service Unavailable'; break;\r\n default: defaultMessage = `Server Error (${status})`;\r\n }\r\n\r\n const finalApiError: ApiError = {\r\n message: responseData?.message || defaultMessage,\r\n status: status,\r\n code: responseData?.code || error.code,\r\n errors: responseData?.errors || [],\r\n };\r\n \r\n return {\r\n data: null, rawResponse: responseData,\r\n error: finalApiError,\r\n validationErrors: finalApiError.errors,\r\n success: false, loading: false, message: finalApiError.message,\r\n };\r\n }\r\n\r\n // الحالة 2.2: خطأ في الشبكة\r\n // داخل هذا البلوك، TypeScript متأكد 100% أن `responseOrError.request` موجود.\r\n if (isNetworkError(responseOrError)) {\r\n const error = responseOrError;\r\n return {\r\n data: null, rawResponse: error.request,\r\n error: { message: 'Network Error: Unable to connect.', status: 0, code: error.code },\r\n success: false, loading: false, message: 'Network Error.',\r\n };\r\n }\r\n \r\n // الحالة 2.3: تم إلغاء الطلب\r\n if (axios.isCancel(responseOrError)) {\r\n return {\r\n data: null, rawResponse: null,\r\n error: { message: 'Request Canceled.', status: 499 },\r\n success: false, loading: false, message: 'Request Canceled.',\r\n };\r\n }\r\n\r\n // ===================================================================================\r\n // #region المسار الأخير: الحالة الاحتياطية\r\n // ===================================================================================\r\n \r\n // لأي خطأ آخر لم تتم معالجته (مثل أخطاء الإعداد)\r\n return {\r\n data: null, rawResponse: responseOrError,\r\n error: { message: 'An unknown error occurred.', status: -1 },\r\n success: false, loading: false, message: 'An unknown error occurred.',\r\n };\r\n};\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // file: src/core/processor.ts (مُحدَّث)\r\n\r\n// import { AxiosResponse } from 'axios';\r\n// import { ApiError, StandardResponse } from '../types';\r\n// import { isApiError, isAxiosResponse } from './utils';\r\n\r\n// export const processResponse = <T>(\r\n// responseOrError: AxiosResponse<any> | ApiError,\r\n// ): StandardResponse<T> => {\r\n// console.log(\"responseOrError [lib]\" , responseOrError)\r\n// console.log(\"responseOrError.data [lib]\" , responseOrError?.status)\r\n\r\n// if (isApiError(responseOrError)) {\r\n// return {\r\n// data: null,\r\n// rawResponse: undefined,\r\n// error: responseOrError,\r\n// validationErrors: responseOrError.errors || [],\r\n// success: false,\r\n// loading: false,\r\n// message: responseOrError.message,\r\n// };\r\n// }\r\n \r\n// if (isAxiosResponse(responseOrError)) {\r\n// const rawData = responseOrError.data;\r\n// const isWrappedResponse = \r\n// rawData && \r\n// typeof rawData.success === 'boolean' && \r\n// rawData.data !== undefined;\r\n \r\n// const finalData: T = isWrappedResponse ? rawData.data : rawData;\r\n// const message = isWrappedResponse ? rawData.message : 'Request successful.';\r\n\r\n// return {\r\n// data: finalData,\r\n// rawResponse: rawData,\r\n// loading: false,\r\n// success: true,\r\n// error: null,\r\n// message: message,\r\n// validationErrors: [],\r\n// };\r\n// }\r\n\r\n// return {\r\n// data: null,\r\n// rawResponse: undefined,\r\n// error: { message: 'An unknown error occurred during response processing.', status: 500 },\r\n// success: false,\r\n// loading: false,\r\n// message: 'An unknown error occurred.',\r\n// validationErrors: []\r\n// };\r\n// };","import axios, { AxiosError, AxiosResponse, CancelTokenSource } from 'axios';\r\nimport { ApiError, QueryOptions, StandardResponse } from '../types';\r\n\r\n// ===================================================================================\r\n// #region Type Guards (التحقق من الأنواع)\r\n// ===================================================================================\r\n\r\n/**\r\n * يتحقق مما إذا كان الكائن هو خطأ مخصص من نوع ApiError.\r\n */\r\nexport function isApiError(obj: any): obj is ApiError {\r\n return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n}\r\n\r\n\r\n// الدوال القديمة يمكن الإبقاء عليها للتوافق أو استبدالها بالكامل\r\nexport function isAxiosError(obj: any): obj is AxiosError {\r\n return obj && obj.isAxiosError === true;\r\n}\r\n\r\nexport function isAxiosResponse(obj: any): obj is AxiosResponse {\r\n return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n}\r\n\r\n\r\n\r\n\r\n// ===================================================================================\r\n// #region URL & Query Handling (معالجة روابط URL والاستعلامات)\r\n// ===================================================================================\r\n\r\n/**\r\n * يبني رابطًا ديناميكيًا عن طريق استبدال المتغيرات المؤقتة مثل {key} بالقيم من كائن.\r\n * @param template - قالب الرابط (مثال: '/users/{userId}/posts').\r\n * @param params - كائن يحتوي على قيم المتغيرات (مثال: { userId: 123 }).\r\n * @returns الرابط النهائي بعد المعالجة.\r\n */\r\nexport function buildDynamicUrl(template: string, params?: Record<string, any>): string {\r\n if (!params) {\r\n return template;\r\n }\r\n return template.replace(/\\{(\\w+)\\}/g, (placeholder, key) => {\r\n return params.hasOwnProperty(key) ? String(params[key]) : placeholder;\r\n });\r\n}\r\n\r\n/**\r\n * [نسخة مطورة] يبني سلسلة استعلام (query string) من كائن الخيارات.\r\n * يدعم الآن الفلاتر المتداخلة (filter[key]=value) والترتيب المتعدد.\r\n * @param options - كائن خيارات الاستعلام (فلترة, ترتيب, ...).\r\n * @returns سلسلة استعلام جاهزة للإضافة للرابط.\r\n */\r\nexport function buildPaginateQuery(options: QueryOptions): string {\r\n const params = new URLSearchParams();\r\n\r\n for (const key in options) {\r\n const value = options[key];\r\n\r\n if (value === null || value === undefined) {\r\n continue; // تجاهل القيم الفارغة\r\n }\r\n\r\n if (key === 'filter' && typeof value === 'object' && !Array.isArray(value)) {\r\n // التعامل مع الفلاتر المتداخلة\r\n for (const filterKey in value) {\r\n const filterValue = value[filterKey];\r\n if (filterValue !== null && filterValue !== undefined) {\r\n params.append(`filter[${filterKey}]`, String(filterValue));\r\n }\r\n }\r\n } else if (key === 'sortBy' && Array.isArray(value)) {\r\n // التعامل مع الترتيب المتعدد\r\n value.forEach(sortItem => {\r\n if (sortItem && sortItem.key && sortItem.direction) {\r\n params.append('sortBy[]', `${sortItem.key}:${sortItem.direction}`);\r\n }\r\n });\r\n } else {\r\n // التعامل مع باقي المعاملات\r\n params.append(key, String(value));\r\n }\r\n }\r\n\r\n return params.toString();\r\n}\r\n\r\n// ===================================================================================\r\n// #region Object & Data Helpers (أدوات مساعدة للكائنات والبيانات)\r\n// ===================================================================================\r\n\r\n/**\r\n * يتحقق مما إذا كانت الاستجابة تحتوي على بيانات قابلة للعرض.\r\n * @param response - كائن StandardResponse.\r\n * @returns `true` إذا كانت البيانات موجودة وليست مصفوفة فارغة.\r\n */\r\nexport function hasData<T>(response: StandardResponse<T> | null | undefined): boolean {\r\n if (!response || !response.success || response.data === null || response.data === undefined) {\r\n return false;\r\n }\r\n if (Array.isArray(response.data) && response.data.length === 0) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * ينشئ نسخة جديدة من كائن مع إزالة كل الخصائص التي قيمتها `null` أو `undefined`.\r\n * @param obj - الكائن المراد تنظيفه.\r\n * @returns نسخة نظيفة من الكائن.\r\n */\r\nexport function cleanObject<T extends Record<string, any>>(obj: T): Partial<T> {\r\n const newObj: Partial<T> = {};\r\n for (const key in obj) {\r\n if (obj[key] !== null && obj[key] !== undefined) {\r\n newObj[key] = obj[key];\r\n }\r\n }\r\n return newObj;\r\n}\r\n\r\n// ===================================================================================\r\n// #region Async Helpers (أدوات مساعدة غير متزامنة)\r\n// ===================================================================================\r\n\r\n/**\r\n * ينشئ دالة تقوم بتأخير تنفيذ الدالة الأصلية حتى انقضاء فترة زمنية معينة\r\n * بعد آخر مرة تم استدعاؤها. مفيدة جدًا لتقليل الطلبات في حقول البحث.\r\n * @param func - الدالة التي سيتم تأخير تنفيذها.\r\n * @param delay - مدة التأخير بالمللي ثانية.\r\n * @returns دالة جديدة قابلة للاستدعاء.\r\n */\r\nexport function debounce<T extends (...args: any[]) => any>(func: T, delay: number): (...args: Parameters<T>) => void {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n return function(...args: Parameters<T>) {\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n timeoutId = setTimeout(() => {\r\n func(...args);\r\n }, delay);\r\n };\r\n}\r\n\r\n/**\r\n * دالة بسيطة لإيقاف التنفيذ لمدة زمنية محددة.\r\n * @param ms - مدة التأخير بالمللي ثانية.\r\n * @returns Promise يكتمل بعد انقضاء المدة.\r\n */\r\nexport function delay(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n// ===================================================================================\r\n// #region Request Cancellation (إدارة إلغاء الطلبات)\r\n// ===================================================================================\r\n\r\nconst cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n/**\r\n * ينشئ cancel token جديد لطلب معين، ويلغي أي طلب قديم بنفس المفتاح.\r\n * يمنع حالات الـ race conditions في المكونات التي ترسل طلبات متتالية.\r\n * @param key - مفتاح فريد لتعريف الطلب (مثال: 'user-search').\r\n * @returns كائن CancelTokenSource من Axios.\r\n */\r\nexport const createCancelToken = (key: string): CancelTokenSource => {\r\n const existingToken = cancelTokens.get(key);\r\n if (existingToken) {\r\n existingToken.cancel('Operation canceled due to a new request.');\r\n }\r\n const source = axios.CancelToken.source();\r\n cancelTokens.set(key, source);\r\n return source;\r\n};\r\n\r\n\r\n\r\n\r\n\r\n// ===================================================================================\r\n// #region Advanced Type Guards (أدوات تحقق متقدمة من الأنواع)\r\n// ===================================================================================\r\n\r\n/**\r\n * [جديد وحاسم] يتحقق مما إذا كان الخطأ هو خطأ من الخادم (يحتوي على `response`).\r\n * الأهم من ذلك، أنه يضيق نوع الخطأ ليضمن وجود `response`.\r\n */\r\nexport function isServerError(error: any): error is AxiosError & { response: AxiosResponse } {\r\n return axios.isAxiosError(error) && error.response !== undefined;\r\n}\r\n\r\n/**\r\n * [جديد وحاسم] يتحقق مما إذا كان الخطأ هو خطأ في الشبكة (لا يحتوي على `response` ولكن يحتوي على `request`).\r\n * يضيق نوع الخطأ ليضمن عدم وجود `response`.\r\n */\r\nexport function isNetworkError(error: any): error is AxiosError & { request: any } {\r\n return axios.isAxiosError(error) && error.response === undefined && error.request !== undefined;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// /**\r\n// * @file src/core/utils.ts\r\n// * @description\r\n// * يحتوي هذا الملف على مجموعة من الدوال المساعدة المستخدمة في جميع أنحاء المكتبة،\r\n// * مثل بناء سلاسل الاستعلام (query strings) وإدارة إلغاء الطلبات،\r\n// * بالإضافة إلى دوال التحقق من الأنواع (type guards).\r\n// */\r\n\r\n// import axios, { CancelTokenSource } from 'axios';\r\n// import { ApiError, QueryOptions } from '../types';\r\n// import { AxiosResponse } from 'axios';\r\n\r\n// // ===================================\r\n// // إدارة إلغاء الطلبات\r\n// // ===================================\r\n// const cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n// export const createCancelToken = (key: string): CancelTokenSource => {\r\n// const existingToken = cancelTokens.get(key);\r\n// if (existingToken) {\r\n// existingToken.cancel('Operation canceled due to a new request.');\r\n// cancelTokens.delete(key);\r\n// }\r\n// const source = axios.CancelToken.source();\r\n// cancelTokens.set(key, source);\r\n// return source;\r\n// };\r\n\r\n\r\n\r\n// interface BuildQueryOptions {\r\n// encode?: boolean;\r\n// }\r\n\r\n// /**\r\n// * يبني سلسلة استعلام (query string) من كائن الخيارات.\r\n// * @param options - كائن خيارات الاستعلام (فلترة, ترتيب, ...).\r\n// * @param config - إعدادات إضافية مثل التحكم في التشفير.\r\n// * @returns سلسلة استعلام جاهزة للإضافة للرابط.\r\n// */\r\n// export function buildPaginateQuery(options: QueryOptions, config: BuildQueryOptions = { encode: true }): string {\r\n// // استخدام URLSearchParams يقوم بالتشفير تلقائيًا وهو الأسلوب القياسي والآمن\r\n// if (config.encode) {\r\n// const params = new URLSearchParams();\r\n// Object.entries(options).forEach(([key, value]) => {\r\n// if (value !== undefined && value !== null) {\r\n// // يمكنك إضافة منطق أكثر تعقيدًا هنا للتعامل مع المصفوفات أو الكائنات\r\n// params.append(key, String(value));\r\n// }\r\n// });\r\n// return params.toString();\r\n// }\r\n\r\n// // منطق بديل إذا كان encode = false (لـ APIs التي تتطلب تنسيقًا خاصًا)\r\n// const parts = Object.entries(options)\r\n// .filter(([, value]) => value !== undefined && value !== null)\r\n// .map(([key, value]) => `${key}=${String(value)}`);\r\n \r\n// return parts.join('&');\r\n// }\r\n\r\n// // ===================================\r\n// // التحقق من الأنواع (Type Guards)\r\n// // ===================================\r\n// export function isApiError(obj: any): obj is ApiError {\r\n// console.log(\"obj in IsApiError\" , obj)\r\n// return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n// }\r\n\r\n// export function isAxiosResponse(obj: any): obj is AxiosResponse {\r\n// return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // import axios, { CancelTokenSource } from 'axios';\r\n// // import { PaginateQueryOptions, ApiError } from '../types';\r\n\r\n// // // === Cancel Token Management ===\r\n// // const cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n// // export const createCancelToken = (key: string): CancelTokenSource => {\r\n// // const existingToken = cancelTokens.get(key);\r\n// // if (existingToken) {\r\n// // existingToken.cancel('Operation canceled due to new request.');\r\n// // cancelTokens.delete(key);\r\n// // }\r\n// // const source = axios.CancelToken.source();\r\n// // cancelTokens.set(key, source);\r\n// // return source;\r\n// // };\r\n\r\n// // export const cancelRequest = (key: string): void => {\r\n// // const source = cancelTokens.get(key);\r\n// // if (source) {\r\n// // source.cancel('Operation canceled by user.');\r\n// // cancelTokens.delete(key);\r\n// // }\r\n// // };\r\n\r\n// // export const cancelAllRequests = (): void => {\r\n// // cancelTokens.forEach(source => source.cancel('All operations canceled.'));\r\n// // cancelTokens.clear();\r\n// // };\r\n\r\n// // // === Query Builder ===\r\n// // export function buildPaginateQuery(query: PaginateQueryOptions): string {\r\n// // const params = new URLSearchParams();\r\n// // if (!query) return '';\r\n\r\n// // if (query.page) params.append('page', query.page.toString());\r\n// // if (query.limit) params.append('limit', query.limit.toString());\r\n// // if (query.search) params.append('search', query.search);\r\n// // if (query.sortBy) {\r\n// // query.sortBy.forEach(sort => params.append('sortBy', `${sort.key}:${sort.direction}`));\r\n// // }\r\n// // if (query.filter) {\r\n// // Object.entries(query.filter).forEach(([field, value]) => {\r\n// // params.append(`filter.${field}`, value);\r\n// // });\r\n// // }\r\n// // const queryString = params.toString();\r\n// // return queryString ? `?${queryString}` : '';\r\n// // }\r\n\r\n// // // === Type Guards ===\r\n// // export function isApiError(obj: any): obj is ApiError {\r\n// // return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n// // }\r\n\r\n// // export function isAxiosResponse(obj: any): obj is any {\r\n// // return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n// // }","/**\r\n * Replaces path placeholders like {key} with values from a params object.\r\n * @param template - The URL template (e.g., '/users/{userId}/posts').\r\n * @param params - An object with keys matching the placeholders (e.g., { userId: 123 }).\r\n * @returns The final, resolved URL string.\r\n */\r\nexport function buildDynamicUrl(template: string, params?: Record<string, any>): string {\r\n if (!params) {\r\n return template;\r\n }\r\n return template.replace(/\\{(\\w+)\\}/g, (placeholder, key) => {\r\n return params.hasOwnProperty(key) ? String(params[key]) : placeholder;\r\n });\r\n}","// file: src/services/crud.ts\r\n\r\nimport { AxiosInstance, AxiosRequestConfig } from 'axios';\r\nimport { ActionOptions, RequestConfig, StandardResponse } from '../types';\r\nimport { processResponse } from '../core/processor';\r\nimport { buildDynamicUrl } from '@/core/buildDynamicUrl';\r\nimport { ActionConfigModule } from '@/types/apiModule.types';\r\n\r\n/**\r\n * A factory function to create a reusable set of API services for a specific endpoint.\r\n * It provides full CRUD operations plus advanced features like bulk deletion and file uploads,\r\n * with intelligent, dynamic URL building.\r\n */\r\nexport function createApiServices<T>(axiosInstance: AxiosInstance, baseEndpoint: string) {\r\n \r\n /**\r\n * Intelligently resolves the final URL for a request.\r\n * Priority:\r\n * 1. A full endpoint override from `config.endpoint`.\r\n * 2. The base endpoint appended with an ID, if provided.\r\n * 3. The base endpoint alone.\r\n * It also replaces dynamic segments like {id} or {tenantId} in the URL.\r\n */\r\n const resolveUrl = (config: ActionOptions = {}, id?: string | number): string => {\r\n const endpointTemplate = config.endpoint || (id != null ? `${baseEndpoint}/{id}` : baseEndpoint);\r\n \r\n // Create a params object for URL building. We include common ID variations.\r\n const params = id != null ? { id, tenant: id, tenantId: id, recordId: id } : {};\r\n \r\n return buildDynamicUrl(endpointTemplate, params);\r\n };\r\n\r\n /**\r\n * Fetches data. Can fetch a list or a single record based on whether an ID is provided.\r\n * This is the primary flexible read operation.\r\n */\r\n const get = async (id?: string | number, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.get(url, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Fetches a list of records using a query string.\r\n * This is specifically for fetching paginated/filtered lists.\r\n */\r\n const getWithQuery = async (query: string, config?: RequestConfig): Promise<StandardResponse<T>> => {\r\n const url = `${baseEndpoint}?${query}`;\r\n try {\r\n const response = await axiosInstance.get(url, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new resource.\r\n */\r\n const post = async (data: Partial<T>, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config);\r\n try {\r\n const response = await axiosInstance.post(url, data, config);\r\n console.log(\"[lib] response POST: \" , response)\r\n console.log(\"[lib] response processResponse POST: \" , processResponse<T>(response))\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n console.log(\"[lib] response error POST: \" , processResponse<T>(error as any))\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Replaces an entire resource.\r\n */\r\n const put = async (id: string | number, data: T, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.put(url, data, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Partially updates a resource.\r\n */\r\n const patch = async (id: string | number, data: Partial<T>, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.patch(url, data, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Deletes a resource.\r\n */\r\n const remove = async (id: string | number, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.delete(url, config);\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Deletes multiple resources in a single request.\r\n */\r\n const bulkDelete = async (ids: Array<string | number>, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config); // Bulk actions typically don't use an ID in the path.\r\n try {\r\n const response = await axiosInstance.delete(url, { data: { ids }, ...config });\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Uploads a file, with optional additional data.\r\n */\r\n const upload = async (file: File, additionalData?: Record<string, any>, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config);\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n if (additionalData) {\r\n Object.keys(additionalData).forEach(key => formData.append(key, String(additionalData[key])));\r\n }\r\n try {\r\n const response = await axiosInstance.post(url, formData, {\r\n ...config, headers: { ...config?.headers, 'Content-Type': 'multipart/form-data' },\r\n });\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n\r\n return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// نفترض أن هذه الدوال موجودة في ملف مساعد\r\n// import { buildDynamicUrl } from '@/core/buildDynamicUrl';\r\n// import { processResponse } from '../core/processor';\r\n\r\n/**\r\n * [نسخة محدثة ومحسّنة]\r\n * دالة عامة وصريحة لتنفيذ أي طلب API.\r\n * تستقبل وسائط منظمة بدلاً من محاولة تخمينها.\r\n * \r\n * @param axiosInstance - نسخة Axios.\r\n * @param baseEndpoint - نقطة النهاية الأساسية للموديول.\r\n * @param actionConfig - إعدادات الإجراء المحدد.\r\n * @param params - كائن يحتوي على جميع البارامترات اللازمة للطلب.\r\n * @returns Promise<StandardResponse<any>>\r\n */\r\nexport async function callDynamicApi(\r\n axiosInstance: AxiosInstance,\r\n baseEndpoint: string,\r\n actionConfig: ActionConfigModule<any, any>,\r\n params: {\r\n pathParams?: Record<string, string | number>;\r\n body?: any;\r\n config?: AxiosRequestConfig;\r\n }\r\n): Promise<StandardResponse<any>> {\r\n \r\n // 1. استخراج البارامترات بشكل صريح وواضح\r\n const { pathParams, body, config } = params;\r\n\r\n // 2. بناء الـ URL الديناميكي\r\n const urlTemplate = `${baseEndpoint}${actionConfig.path}`;\r\n // افترض وجود هذه الدوال أو قم بإنشائها\r\n const finalUrl = buildDynamicUrl(urlTemplate, pathParams || {}); \r\n\r\n try {\r\n const { method } = actionConfig;\r\n let response;\r\n\r\n // 3. تنفيذ الطلب بناءً على نوع الـ Method\r\n // أصبح المنطق الآن أكثر نظافة ووضوحًا\r\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\r\n response = await axiosInstance[method.toLowerCase() as 'post'](finalUrl, body, config);\r\n } else if (method === 'DELETE') {\r\n // DELETE قد يحتوي على body في بعض الحالات (مثل bulk)\r\n response = await axiosInstance.delete(finalUrl, { data: body, ...config });\r\n } else { // GET\r\n // في GET، الجسم (body) يمثل معاملات الاستعلام (query params)\r\n response = await axiosInstance.get(finalUrl, { params: body, ...config });\r\n }\r\n \r\n return processResponse(response);\r\n } catch (error) {\r\n // معالجة الأخطاء تبقى كما هي\r\n return processResponse(error as any);\r\n }\r\n}\r\n\r\n\r\n\r\n\r\n// import { AxiosInstance } from 'axios';\r\n// import { ActionOptions, RequestConfig, StandardResponse } from '../types';\r\n// import { processResponse } from '../core/processor';\r\n\r\n// type CrudRequestConfig = RequestConfig & ActionOptions;\r\n\r\n// /**\r\n// * دالة مصنع (Factory Function) لإنشاء مجموعة خدمات API قابلة لإعادة الاستخدام لنقطة نهاية (endpoint) محددة.\r\n// * توفر عمليات CRUD كاملة بالإضافة إلى ميزات متقدمة مثل الحذف الجماعي ورفع الملفات.\r\n// */\r\n// export function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string) {\r\n \r\n// // --- Read Operations ---\r\n// const get = async (id?: string, config?: RequestConfig): Promise<StandardResponse<T | T[]>> => {\r\n// const url = id ? `${endpoint}/${id}` : endpoint;\r\n// try {\r\n// const response = await axiosInstance.get(url, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// const getWithQuery = async (query: string, config?: RequestConfig): Promise<StandardResponse<T[]>> => {\r\n// try {\r\n// const response = await axiosInstance.get(`${endpoint}${query}`, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// // --- Write Operations ---\r\n// const post = async (data: Partial<T>, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || endpoint;\r\n// try {\r\n// const response = await axiosInstance.post(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة PUT\r\n// * تستخدم لاستبدال مورد بالكامل.\r\n// * لاحظ أن `data` من النوع `T` وليس `Partial<T>` لأن PUT يتوقع المورد كاملاً.\r\n// */\r\n// const put = async (id: string, data: T, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.put(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// const patch = async (id: string, data: Partial<T>, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.patch(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// const remove = async (id: string, config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.delete(finalUrl, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// // --- Advanced Operations ---\r\n\r\n// /**\r\n// * ✅ NEW: إضافة دالة للحذف الجماعي (Bulk Delete)\r\n// * مفيدة جدًا لحذف عدة عناصر في طلب واحد لتحسين الأداء.\r\n// * ترسل مصفوفة من الـ IDs في جسم الطلب (request body).\r\n// */\r\n// const bulkDelete = async (ids: string[], config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}`; // يمكن تخصيص المسار\r\n// try {\r\n// // Axios's delete method can send a body via the config object\r\n// const response = await axiosInstance.delete(finalUrl, { data: { ids }, ...config });\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// /**\r\n// * ✅ NEW: إضافة دالة لرفع الملفات (File Upload)\r\n// * تتعامل مع `FormData` لرفع الملفات، وهي ميزة أساسية في معظم التطبيقات.\r\n// * يمكن إرسال بيانات إضافية مع الملف.\r\n// */\r\n// const upload = async (file: File, additionalData?: Record<string, any>, config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}`;\r\n \r\n// const formData = new FormData();\r\n// formData.append('file', file); // 'file' هو اسم الحقل الشائع، يمكن تغييره\r\n \r\n// if (additionalData) {\r\n// Object.keys(additionalData).forEach(key => {\r\n// formData.append(key, additionalData[key]);\r\n// });\r\n// }\r\n\r\n// try {\r\n// const response = await axiosInstance.post(finalUrl, formData, {\r\n// ...config,\r\n// headers: {\r\n// ...config?.headers,\r\n// 'Content-Type': 'multipart/form-data',\r\n// },\r\n// });\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","\r\nimport { AxiosInstance, Method } from 'axios';\r\nimport { processResponse } from '../core/processor';\r\nimport { RequestConfig, StandardResponse } from '@/types';\r\n\r\n/**\r\n * Defines a single custom API action.\r\n * @template TRequest - The type of the data sent in the request body/params.\r\n * @template TResponse - The type of the data expected in the successful API response.\r\n */\r\ntype ApiAction<TRequest, TResponse> = (\r\n payload: TRequest,\r\n config?: RequestConfig\r\n) => Promise<StandardResponse<TResponse>>;\r\n\r\n// type ApiAction<TRequest, TResponse> = TRequest extends void\r\n// ? (payload?: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>\r\n// : (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;\r\n \r\nfunction createAction<TRequest, TResponse>(\r\n axiosInstance: AxiosInstance,\r\n method: Method,\r\n endpoint: string,\r\n // log?:boolean,\r\n): ApiAction<TRequest, TResponse> {\r\n return async (payload: TRequest, config?: RequestConfig) => {\r\n// return async (payload: TRequest = {} as TRequest, config?: RequestConfig) => {\r\n // log && console.log(`*[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} **** [${payload}]** `)\r\n try {\r\n // log && console.log(`**[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]:${endpoint} **** [action try req]** `)\r\n const response = await axiosInstance.request<any>({\r\n url: endpoint,\r\n method,\r\n ...(method.toUpperCase() === 'GET' ? { params: payload } : { data: payload }),\r\n ...config,\r\n });\r\n // log && console.log(`***[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} ***\\n ${response}*** `)\r\n return processResponse<TResponse>(response);\r\n } catch (error: any) {\r\n return processResponse(error);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * A factory function to create a collection of typed, custom API actions.\r\n * \r\n * @template TActions - An object type where keys are action names and values are objects\r\n * defining the endpoint, method, and types for that action.\r\n * @param axiosInstance - The configured Axios instance from `createApiClient`.\r\n * @param actionsConfig - An object defining the configuration for each custom action.\r\n * @returns A fully-typed object of executable API action functions.\r\n * \r\n * @example\r\n * const authActions = createApiActions(apiClient, {\r\n * login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },\r\n * getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }\r\n * });\r\n * \r\n * // Usage:\r\n * const result = await authActions.login({ email: '..', password: '..' });\r\n */\r\nexport function createApiActions<\r\n TActions extends Record<string, { \r\n method: Method; \r\n endpoint: string; \r\n requestType: any; \r\n responseType: any;\r\n log?:boolean\r\n }>\r\n>(\r\n axiosInstance: AxiosInstance,\r\n actionsConfig: TActions\r\n): { [K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']> } {\r\n \r\n const actions = {} as any;\r\n\r\n for (const actionName in actionsConfig) {\r\n if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {\r\n const { method, endpoint , log} = actionsConfig[actionName];\r\n actions[actionName] = createAction(axiosInstance, method, endpoint);\r\n }\r\n }\r\n\r\n return actions;\r\n}","// file: src/core/globalStateManager.ts\r\n\r\nimport { ActionStateModule } from \"@/types\";\r\n\r\n\r\ntype CacheItem = {\r\n state: ActionStateModule<any>;\r\n listeners: Set<() => void>;\r\n};\r\n\r\nconst createInitialState = (): ActionStateModule<any> => ({\r\n data: null,\r\n links: undefined,\r\n meta: undefined,\r\n error: null,\r\n loading: false,\r\n success: false,\r\n called: false,\r\n isStale: false,\r\n message: undefined,\r\n validationErrors: [],\r\n rawResponse: null,\r\n});\r\n\r\nclass GlobalStateManager {\r\n private store = new Map<string, CacheItem>();\r\n\r\n /**\r\n * يحصل على لقطة (snapshot) للحالة الحالية لمفتاح معين.\r\n */\r\n public getSnapshot<T>(key: string): ActionStateModule<T> {\r\n return this.store.get(key)?.state as ActionStateModule<T> ?? createInitialState();\r\n }\r\n\r\n /**\r\n * يسجل دالة callback للاستماع إلى التغييرات على مفتاح معين.\r\n * @returns دالة لإلغاء الاشتراك.\r\n */\r\n public subscribe(key: string, callback: () => void): () => void {\r\n if (!this.store.has(key)) {\r\n this.store.set(key, { state: createInitialState(), listeners: new Set() });\r\n }\r\n const item = this.store.get(key)!;\r\n item.listeners.add(callback);\r\n\r\n return () => {\r\n item.listeners.delete(callback);\r\n };\r\n }\r\n\r\n /**\r\n * يحدّث الحالة لمفتاح معين ويقوم بإعلام جميع المشتركين.\r\n */\r\n public setState<T>(key: string, updater: (prevState: ActionStateModule<T>) => ActionStateModule<T>): void {\r\n const currentState = this.getSnapshot<T>(key);\r\n const newState = updater(currentState);\r\n\r\n // نضمن وجود العنصر قبل التحديث\r\n if (!this.store.has(key)) {\r\n this.store.set(key, { state: newState, listeners: new Set() });\r\n } else {\r\n this.store.get(key)!.state = newState;\r\n }\r\n\r\n // إعلام المشتركين\r\n this.store.get(key)!.listeners.forEach(listener => listener());\r\n }\r\n\r\n /**\r\n * يجعل البيانات المرتبطة بمفتاح معين \"قديمة\" (stale).\r\n */\r\n public invalidate(key: string): void {\r\n const state = this.getSnapshot(key);\r\n // يبطل فقط إذا كانت هناك بيانات بالفعل\r\n if (state.called) {\r\n this.setState(key, (prev) => ({ ...prev, isStale: true }));\r\n }\r\n }\r\n\r\n\r\n /**\r\n * [نسخة محدثة وأكثر قوة]\r\n * يجعل كل البيانات التي تبدأ بمفتاح معين \"قديمة\" (stale).\r\n * @example invalidateByPrefix('myModule/list::') سيبطل كل صفحات القائمة.\r\n */\r\n public invalidateByPrefix(prefix: string): void {\r\n this.store.forEach((value, key) => {\r\n if (key.startsWith(prefix)) {\r\n const state = this.getSnapshot(key);\r\n // يبطل فقط إذا كانت هناك بيانات بالفعل\r\n if (state.called) {\r\n this.setState(key, (prev) => ({ ...prev, isStale: true }));\r\n }\r\n }\r\n });\r\n }\r\n\r\n\r\n /**\r\n * Serializes the current state of the query store into a JSON string.\r\n * This is used on the server to pass the initial state to the client.\r\n * @returns A JSON string representing the dehydrated state.\r\n */\r\n public dehydrate(): string {\r\n const stateToPersist: Record<string, any> = {};\r\n this.store.forEach((value, key) => {\r\n // Dehydrate only if the state has been populated.\r\n if (value.state.called) {\r\n stateToPersist[key] = value.state;\r\n }\r\n });\r\n return JSON.stringify(stateToPersist);\r\n }\r\n\r\n /**\r\n * Merges a dehydrated state object into the current store.\r\n * This is used on the client to hydrate the state received from the server.\r\n * @param hydratedState - A JSON string from the `dehydrate` method.\r\n */\r\n public rehydrate(hydratedState: string): void {\r\n try {\r\n const parsedState = JSON.parse(hydratedState);\r\n for (const key in parsedState) {\r\n // We call setState to ensure any listening components are notified.\r\n // This prevents race conditions where a component might subscribe before rehydration is complete.\r\n this.setState(key, () => parsedState[key]);\r\n }\r\n } catch (e) {\r\n console.error(\"[api-core-lib] Failed to rehydrate state:\", e);\r\n }\r\n }\r\n\r\n \r\n}\r\n\r\nexport const globalStateManager = new GlobalStateManager();","interface CacheItem<T> {\r\n data: T;\r\n timestamp: number;\r\n duration: number; \r\n}\r\n\r\nexport class CacheManager {\r\n private cache = new Map<string, CacheItem<any>>();\r\n private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n set<T>(key: string, data: T, duration?: number): void {\r\n this.cache.set(key, {\r\n data,\r\n timestamp: Date.now(),\r\n duration: duration || this.defaultDuration,\r\n });\r\n }\r\n\r\n get<T>(key: string): T | null {\r\n const item = this.cache.get(key);\r\n if (!item) return null;\r\n\r\n const isExpired = Date.now() - item.timestamp > item.duration;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return null;\r\n }\r\n return item.data as T;\r\n }\r\n\r\n /**\r\n * [FIX] تم تحويلها إلى دالة عامة (generic) لتعيد النوع الصحيح.\r\n * الآن بدلًا من إرجاع CacheItem<unknown>، ستُرجع CacheItem<T>.\r\n */\r\n getWithMeta<T>(key: string): CacheItem<T> | null {\r\n const item = this.cache.get(key);\r\n if (!item) return null;\r\n\r\n const isExpired = Date.now() - item.timestamp > item.duration;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return null;\r\n }\r\n \r\n // نقوم بإخبار TypeScript أننا نثق بأن هذا العنصر هو من النوع T\r\n return item as CacheItem<T>;\r\n }\r\n\r\n delete(key: string): void {\r\n this.cache.delete(key);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n \r\n invalidateByPrefix(prefix: string): void {\r\n const keysToDelete: string[] = [];\r\n for (const key of this.cache.keys()) {\r\n if (key.startsWith(prefix)) {\r\n keysToDelete.push(key);\r\n }\r\n }\r\n keysToDelete.forEach(key => this.cache.delete(key));\r\n console.log(`Invalidated ${keysToDelete.length} cache entries with prefix: ${prefix}`);\r\n }\r\n}\r\n\r\nexport const cacheManager = new CacheManager();\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// interface CacheItem<T> {\r\n// data: T;\r\n// timestamp: number;\r\n// duration: number; \r\n// }\r\n\r\n// export class CacheManager {\r\n// private cache = new Map<string, CacheItem<any>>();\r\n// private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n// set<T>(key: string, data: T, duration?: number): void {\r\n// this.cache.set(key, {\r\n// data,\r\n// timestamp: Date.now(),\r\n// duration: duration || this.defaultDuration,\r\n// });\r\n// }\r\n\r\n// // هذه الدالة تبقى كما هي للتوافق مع الأجزاء الأخرى من المكتبة\r\n// get<T>(key: string): T | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key);\r\n// return null;\r\n// }\r\n// return item.data;\r\n// }\r\n\r\n// /**\r\n// * [دالة جديدة] تعيد عنصر الـ Cache بالكامل (البيانات + معلومات إضافية)\r\n// * دون حذفه عند انتهاء الصلاحية. هذا يسمح لنا بتطبيق منطق stale-while-revalidate.\r\n// */\r\n// getWithMeta<T>(key: string): CacheItem<T> | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// // لا نحذف العنصر حتى لو انتهت صلاحيته، فقط نرجعه\r\n// // المتلقي هو من يقرر ما سيفعله به\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key); // سنبقي على منطق الحذف للحفاظ على نظافة الكاش\r\n// return null;\r\n// }\r\n \r\n// return item as CacheItem<T>;\r\n// }\r\n\r\n// delete(key: string): void {\r\n// this.cache.delete(key);\r\n// }\r\n\r\n// clear(): void {\r\n// this.cache.clear();\r\n// }\r\n \r\n// /**\r\n// * [دالة جديدة] تقوم بإبطال (حذف) جميع عناصر الـ Cache التي تبدأ ببادئة معينة.\r\n// * أساسية لعملية invalidation التلقائية.\r\n// */\r\n// invalidateByPrefix(prefix: string): void {\r\n// const keysToDelete: string[] = [];\r\n// for (const key of this.cache.keys()) {\r\n// if (key.startsWith(prefix)) {\r\n// keysToDelete.push(key);\r\n// }\r\n// }\r\n// keysToDelete.forEach(key => this.cache.delete(key));\r\n// console.log(`Invalidated ${keysToDelete.length} cache entries with prefix: ${prefix}`);\r\n// }\r\n// }\r\n\r\n// // سنستخدم نفس النسخة الوحيدة (Singleton)\r\n// export const cacheManager = new CacheManager();\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// interface CacheItem<T> {\r\n// data: T;\r\n// timestamp: number;\r\n// duration: number;\r\n// }\r\n\r\n// export class CacheManager {\r\n// private cache = new Map<string, CacheItem<any>>();\r\n// private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n// set<T>(key: string, data: T, duration?: number): void {\r\n// this.cache.set(key, {\r\n// data,\r\n// timestamp: Date.now(),\r\n// duration: duration || this.defaultDuration,\r\n// });\r\n// }\r\n\r\n// get<T>(key: string): T | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key);\r\n// return null;\r\n// }\r\n// return item.data;\r\n// }\r\n\r\n// delete(key: string): void {\r\n// this.cache.delete(key);\r\n// }\r\n\r\n// clear(): void {\r\n// this.cache.clear();\r\n// }\r\n// }\r\n\r\n// export const cacheManager = new CacheManager();","// file: src/hooks/useApi.ts\r\n\r\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { createApiServices } from '../services/crud';\r\nimport { buildPaginateQuery } from '../core/utils'; // تأكد من استيراد الدالة المحدثة\r\nimport { UseApiConfig, QueryOptions, StandardResponse, ApiError, UseApiQuery, UseApiState } from '../types'; // تأكد من استيراد الأنواع من الملف الموحد\r\n\r\n// دالة مساعدة لبناء المسارات الديناميكية (إذا لم تكن في utils)\r\nfunction buildDynamicPath(template: string, params?: Record<string, string | number>): string {\r\n if (!params) return template;\r\n let path = template;\r\n for (const key in params) {\r\n path = path.replace(new RegExp(`{${key}}`, 'g'), String(params[key]));\r\n }\r\n return path;\r\n}\r\n\r\nexport function useApi<T>(\r\n axiosInstance: AxiosInstance,\r\n config: UseApiConfig<T>\r\n): any { // يمكنك تحديد نوع الإرجاع بشكل أدق إذا أردت\r\n const {\r\n endpoint,\r\n pathParams,\r\n // [REMOVE] لم نعد بحاجة لهذا الخيار\r\n // encodeQuery = true, \r\n initialData,\r\n initialQuery = {},\r\n enabled = true,\r\n refetchAfterChange = true,\r\n requestConfig,\r\n onSuccess,\r\n onError,\r\n } = config;\r\n\r\n const finalEndpoint = useMemo(\r\n () => buildDynamicPath(endpoint, pathParams),\r\n [endpoint, pathParams]\r\n );\r\n\r\n const [state, setState] = useState<UseApiState<T>>({\r\n data: initialData ?? null, rawResponse: null, loading: enabled, error: null,\r\n success: false, message: undefined, validationErrors: undefined\r\n });\r\n\r\n const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n\r\n const apiServices = useMemo(\r\n () => createApiServices<T>(axiosInstance, finalEndpoint),\r\n [axiosInstance, finalEndpoint]\r\n );\r\n\r\n const savedOnSuccess = useRef(onSuccess);\r\n const savedOnError = useRef(onError);\r\n\r\n useEffect(() => {\r\n savedOnSuccess.current = onSuccess;\r\n savedOnError.current = onError;\r\n }, [onSuccess, onError]);\r\n\r\n const fetchData = useCallback(async (currentQueryOptions?: QueryOptions) => {\r\n setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n try {\r\n const finalQuery = currentQueryOptions || queryOptions;\r\n const hasQueryParams = Object.keys(finalQuery).length > 0;\r\n\r\n // [FIX] تم تحديث الاستدعاء ليقبل وسيطًا واحدًا فقط\r\n const queryString = buildPaginateQuery(finalQuery);\r\n\r\n const result = hasQueryParams\r\n ? await apiServices.getWithQuery(queryString, requestConfig)\r\n : await apiServices.get(undefined, requestConfig);\r\n \r\n setState(result);\r\n\r\n if (result.success && savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Fetch successful!', result.data ?? undefined);\r\n } else if (!result.success && savedOnError.current) {\r\n savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n }\r\n } catch (e: any) {\r\n const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred.\" };\r\n setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n }\r\n }, [apiServices, queryOptions, requestConfig]);\r\n\r\n useEffect(() => {\r\n if (enabled) {\r\n fetchData();\r\n }\r\n }, [enabled, fetchData]);\r\n\r\n const handleActionResult = useCallback(async (actionPromise: Promise<StandardResponse<any>>) => {\r\n setState(prev => ({ ...prev, loading: true }));\r\n \r\n try {\r\n const result = await actionPromise;\r\n if (result.success) {\r\n if (savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Action successful!', result.data ?? undefined);\r\n }\r\n if (refetchAfterChange) {\r\n await fetchData(queryOptions);\r\n } else {\r\n setState(prev => ({ ...prev, ...result, loading: false })); // تحديث الحالة بالنتيجة إذا لم يكن هناك refetch\r\n }\r\n } else {\r\n if (savedOnError.current) {\r\n savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n }\r\n setState(prev => ({ ...prev, loading: false, error: result.error, success: false }));\r\n }\r\n return result;\r\n } catch(e: any) {\r\n const apiError: ApiError = { status: 500, message: e.message || \"An unexpected error occurred during action.\" };\r\n setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n return { data: null, error: apiError, loading: false, success: false, rawResponse: e } as StandardResponse<any>;\r\n }\r\n }, [refetchAfterChange, fetchData, queryOptions]);\r\n\r\n const actions = {\r\n fetch: fetchData,\r\n create: (newItem: any, options?: any) => handleActionResult(apiServices.post(newItem, options)),\r\n put: (id: any, item: any, options?: any) => handleActionResult(apiServices.put(id, item, options)),\r\n update: (id: any, updatedItem: any, options?: any) => handleActionResult(apiServices.patch(id, updatedItem, options)),\r\n remove: (id: any, options?: any) => handleActionResult(apiServices.remove(id, options)),\r\n bulkRemove: (ids: any, options?: any) => handleActionResult(apiServices.bulkDelete(ids, options)),\r\n };\r\n\r\n const query: UseApiQuery = {\r\n options: queryOptions,\r\n setOptions: setQueryOptions,\r\n setPage: (page: number) => setQueryOptions(prev => ({ ...prev, page })),\r\n setLimit: (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit })),\r\n setSearchTerm: (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search })),\r\n setSorting: (sortBy: any) => setQueryOptions(prev => ({ ...prev, sortBy })),\r\n setFilters: (filter: any) => setQueryOptions(prev => ({ ...prev, page: 1, filter })),\r\n setQueryParam: (key: any, value: any) => setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : value })),\r\n reset: () => setQueryOptions(initialQuery),\r\n };\r\n\r\n return { state, setState, actions, query };\r\n}\r\n\r\n\r\n\r\n\r\n// // file: src/hooks/useApi.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { createApiServices } from '../services/crud';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { UseApiConfig, QueryOptions, StandardResponse, ApiError } from '../types';\r\n// import { UseApiActions, UseApiQuery, UseApiReturn, UseApiState } from '../types/useApi.types';\r\n\r\n// export function useApi<T>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ): UseApiReturn<T> { \r\n// const {\r\n// endpoint, initialData, initialQuery = {}, enabled = true,\r\n// refetchAfterChange = true, requestConfig, onSuccess, onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<UseApiState<T>>({\r\n// data: initialData ?? null, rawResponse: null, loading: enabled, error: null,\r\n// success: false, message: undefined, validationErrors: undefined\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n// const savedOnSuccess = useRef(onSuccess);\r\n// const savedOnError = useRef(onError);\r\n\r\n// useEffect(() => {\r\n// savedOnSuccess.current = onSuccess;\r\n// savedOnError.current = onError;\r\n// }, [onSuccess, onError]);\r\n\r\n// const fetchData = useCallback(async () => {\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// try {\r\n// const hasQueryParams = Object.keys(queryOptions).length > 0;\r\n// const queryString = buildPaginateQuery(queryOptions);\r\n\r\n// const result = hasQueryParams\r\n// ? await apiServices.getWithQuery(queryString, requestConfig)\r\n// : await apiServices.get(undefined, requestConfig);\r\n \r\n// setState(result);\r\n\r\n// if (result.success && savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Fetch successful!', result.data ?? undefined);\r\n// } else if (!result.success && savedOnError.current) {\r\n// savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// } catch (e: any) {\r\n// console.error(\"[useApi Fetch Error] An unhandled exception occurred:\", e);\r\n// const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred.\" };\r\n// setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// }\r\n// }, [apiServices, queryOptions, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// }, [enabled, fetchData]);\r\n\r\n// const handleActionResult = useCallback(async (actionPromise: Promise<StandardResponse<any>>) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n \r\n// // ✅ [الإصلاح الرئيسي 2]: التعامل مع الوعد نفسه، وليس فقط نتيجته\r\n// // هذا يضمن أننا نلتقط الأخطاء حتى لو لم تُرجع `StandardResponse`\r\n// try {\r\n// const result = await actionPromise;\r\n\r\n// if (result.success) {\r\n// if (savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Action successful!', result.data ?? undefined);\r\n// }\r\n// if (refetchAfterChange) {\r\n// setQueryOptions(prev => ({ ...prev }));\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false }));\r\n// }\r\n// } else {\r\n// if (savedOnError.current) {\r\n// savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n// setState(prev => ({ ...prev, loading: false, error: result.error, success: false }));\r\n// }\r\n// return result;\r\n\r\n// } catch(e: any) {\r\n// console.error(\"[useApi Action Error] An unhandled exception occurred:\", e);\r\n// const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred during action.\" };\r\n// setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// // إرجاع استجابة خطأ موحدة\r\n// return { data: null, error: apiError, loading: false, success: false, rawResponse: e } as StandardResponse<any>;\r\n// }\r\n// }, [refetchAfterChange]);\r\n\r\n// const actions: UseApiActions<T> = {\r\n// fetch: fetchData,\r\n// create: (newItem, options) => handleActionResult(apiServices.post(newItem as any, options)),\r\n// put: (id, item, options) => handleActionResult(apiServices.put(id, item as any, options)),\r\n// update: (id, updatedItem, options) => handleActionResult(apiServices.patch(id, updatedItem as any, options)),\r\n// remove: (id, options) => handleActionResult(apiServices.remove(id, options)),\r\n// bulkRemove: (ids, options) => handleActionResult(apiServices.bulkDelete(ids, options)),\r\n// };\r\n\r\n// const query: UseApiQuery = {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage: (page: number) => setQueryOptions(prev => ({ ...prev, page })),\r\n// setLimit: (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit })),\r\n// setSearchTerm: (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search })),\r\n// setSorting: (sortBy) => setQueryOptions(prev => ({ ...prev, sortBy })),\r\n// setFilters: (filter) => setQueryOptions(prev => ({ ...prev, page: 1, filter })),\r\n// setQueryParam: (key, value) => setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : value })),\r\n// reset: () => setQueryOptions(initialQuery),\r\n// };\r\n\r\n// return { state, setState, actions, query };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { createApiServices } from '../services/crud';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { UseApiConfig, QueryOptions, ActionOptions, StandardResponse } from '../types';\r\n// import { UseApiActions, UseApiQuery, UseApiReturn, UseApiState } from './useApi.types';\r\n\r\n\r\n// // The generic constraint ensures that the data type has an 'id' property.\r\n// export function useApi<T extends { id?: string | number }>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ): UseApiReturn<T> { \r\n// const {\r\n// endpoint,\r\n// initialData,\r\n// initialQuery = { limit: 10 },\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<UseApiState<T>>({\r\n// data: initialData ?? null,\r\n// rawResponse: null,\r\n// loading: enabled,\r\n// error: null,\r\n// success: false,\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// const fetch = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// // Set loading state but keep previous data for a better UX during refetch\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// const fetchData = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// // Set loading state but keep previous data for a better UX during refetch\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// // fetchData is memoized, so this effect is safe.\r\n// }, [enabled, queryOptions, fetchData]);\r\n\r\n// // --- Actions ---\r\n// // We define a helper to reduce repetition in action handlers\r\n// const handleActionResult = async (\r\n// actionPromise: Promise<StandardResponse<any>>,\r\n// successMessage: string,\r\n// errorMessage: string\r\n// ) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await actionPromise;\r\n// if (result.success) {\r\n// if (refetchAfterChange) {\r\n// // We don't need to wait for fetchData, it will update state when it's done.\r\n// fetchData();\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false }));\r\n// }\r\n// if (onSuccess) onSuccess(result.message || successMessage, result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || errorMessage, result.error ?? undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const createItem: UseApiActions<T>['create'] = (newItem, options) => {\r\n// return handleActionResult(\r\n// apiServices.post(newItem, options),\r\n// 'Item created successfully!',\r\n// 'Create failed'\r\n// );\r\n// };\r\n \r\n// const putItem: UseApiActions<T>['put'] = (id, item, options) => {\r\n// return handleActionResult(\r\n// apiServices.put(String(id), item, options),\r\n// 'Item replaced successfully!',\r\n// 'Replace failed'\r\n// );\r\n// };\r\n\r\n// const updateItem: UseApiActions<T>['update'] = (id, updatedItem, options) => {\r\n// return handleActionResult(\r\n// apiServices.patch(String(id), updatedItem, options),\r\n// 'Item updated successfully!',\r\n// 'Update failed'\r\n// );\r\n// };\r\n\r\n// const deleteItem: UseApiActions<T>['remove'] = (id, options) => {\r\n// return handleActionResult(\r\n// apiServices.remove(String(id), options),\r\n// 'Item deleted successfully!',\r\n// 'Delete failed'\r\n// );\r\n// };\r\n \r\n// const bulkDeleteItem: UseApiActions<T>['bulkRemove'] = (ids, options) => {\r\n// return handleActionResult(\r\n// apiServices.bulkDelete(ids.map(String), options),\r\n// 'Items deleted successfully!',\r\n// 'Bulk delete failed'\r\n// );\r\n// };\r\n\r\n// const uploadFile: UseApiActions<T>['upload'] = (file, additionalData, options) => {\r\n// return handleActionResult(\r\n// apiServices.upload(file, additionalData, options),\r\n// 'File uploaded successfully!',\r\n// 'Upload failed'\r\n// );\r\n// };\r\n\r\n// // --- Query Controls ---\r\n// const setPage = (page: number) => setQueryOptions(prev => ({ ...prev, page }));\r\n// const setLimit = (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit }));\r\n// const setSearchTerm = (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search }));\r\n// const setSorting = (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setQueryOptions(prev => ({ ...prev, sortBy }));\r\n// const setFilters = (filter: Record<string, any>) => setQueryOptions(prev => ({ ...prev, page: 1, filter }));\r\n// const setQueryParam = (key: string, value: any) => {\r\n// setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? prev.page : value }));\r\n// };\r\n// const resetQuery = () => setQueryOptions(initialQuery);\r\n \r\n// // Assemble the final return object, ensuring it matches the UseApiReturn type.\r\n// const actions: UseApiActions<T> = {\r\n// // refetch: fetchData,\r\n// fetch: fetchData,\r\n// create: createItem,\r\n// put: putItem,\r\n// update: updateItem,\r\n// remove: deleteItem,\r\n// bulkRemove: bulkDeleteItem,\r\n// upload: uploadFile,\r\n// };\r\n\r\n// const query: UseApiQuery = {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage,\r\n// setLimit,\r\n// setSearchTerm,\r\n// setSorting,\r\n// setFilters,\r\n// setQueryParam,\r\n// reset: resetQuery,\r\n// };\r\n\r\n// return {\r\n// state,\r\n// setState,\r\n// actions,\r\n// query, // Fixed typo from 'querry' to 'query'\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // في ملف: useApi.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { createApiServices } from '../services/crud';\r\n// import { ApiError, QueryOptions, StandardResponse, UseApiConfig, ActionOptions } from '../types';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { AxiosInstance } from 'axios';\r\n\r\n// export function useApi<T extends { id?: string | number }>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ) {\r\n// const {\r\n// endpoint,\r\n// initialData,\r\n// initialQuery = { limit: 10 },\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<StandardResponse<T | T[]>>({\r\n// data: initialData || null,\r\n// rawResponse: null,\r\n// loading: enabled,\r\n// error: null,\r\n// success: false,\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// // --- دالة جلب البيانات ---\r\n// const fetchData = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// setState(prev => ({ ...prev, data: null, loading: true, error: null }));\r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error || undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// // eslint-disable-next-line react-hooks/exhaustive-deps\r\n// }, [enabled, queryOptions]);\r\n\r\n// // --- دوال الأكشن (Actions) ---\r\n// const createItem = async (newItem: Partial<T>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.post(newItem, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item created successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Create failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة putItem (لعمليات PUT)\r\n// */\r\n// const putItem = async (id: string, item: T, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.put(id, item, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item replaced successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Replace failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const updateItem = async (id: string, updatedItem: Partial<T>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.patch(id, updatedItem, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item updated successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Update failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const deleteItem = async (id: string, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.remove(id, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item deleted successfully!');\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Delete failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة للحذف الجماعي\r\n// */\r\n// const bulkDeleteItem = async (ids: string[], options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.bulkDelete(ids, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Items deleted successfully!');\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Bulk delete failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة لرفع الملفات\r\n// * لا تقوم بإعادة جلب البيانات افتراضيًا لأن استجابة الرفع قد تكون مختلفة.\r\n// */\r\n// const uploadFile = async (file: File, additionalData?: Record<string, any>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.upload(file, additionalData, options);\r\n// if (result.success) {\r\n// // عادة لا نحتاج لإعادة الجلب هنا، لكن يمكن التحكم في ذلك عبر `refetchAfterChange`\r\n// if (refetchAfterChange && options?.refetch !== false) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'File uploaded successfully!', result.data);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Upload failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// // --- دوال التحكم في الاستعلام ---\r\n// const setPage = (page: number) => setQueryOptions(prev => ({ ...prev, page }));\r\n// const setLimit = (limit: number) => setQueryOptions(prev => ({ ...prev, limit, page: 1 }));\r\n// const setSearchTerm = (search: string) => setQueryOptions(prev => ({ ...prev, search, page: 1 }));\r\n// const setSorting = (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setQueryOptions(prev => ({ ...prev, sortBy }));\r\n// const setFilters = (filter: Record<string, any>) => setQueryOptions(prev => ({ ...prev, filter, page: 1 }));\r\n// const setQueryParam = (key: string, value: any) => {\r\n// setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : prev.page }));\r\n// };\r\n// const resetQuery = () => setQueryOptions(initialQuery);\r\n\r\n// return {\r\n// state,\r\n// setState,\r\n// actions: {\r\n// fetch: fetchData,\r\n// create: createItem,\r\n// put: putItem, // <-- NEW\r\n// update: updateItem,\r\n// remove: deleteItem,\r\n// bulkRemove: bulkDeleteItem, // <-- NEW\r\n// upload: uploadFile, // <-- NEW\r\n// },\r\n// querry: {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage,\r\n// setLimit,\r\n// setSearchTerm,\r\n// setSorting,\r\n// setFilters,\r\n// setQueryParam,\r\n// reset: resetQuery,\r\n// },\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","// file: src/hooks/useApiRecord.ts\r\n\r\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { UseApiRecordActions, UseApiRecordConfig, UseApiRecordReturn, UseApiRecordState } from '@/types/useApiRecord.types';\r\nimport { createApiServices } from '@/services/crud';\r\nimport { ActionOptions, ApiError, StandardResponse } from '@/types';\r\n\r\n/**\r\n * A helper function to build a dynamic URL path by replacing placeholders.\r\n * @param template The URL template with placeholders (e.g., 'v1/users/{userId}').\r\n * @param params An object with keys matching the placeholders.\r\n * @returns The final, resolved URL path.\r\n */\r\nfunction buildDynamicPath(template: string, params?: Record<string, string | number>): string {\r\n if (!params) return template;\r\n let path = template;\r\n for (const key in params) {\r\n path = path.replace(new RegExp(`{${key}}`, 'g'), String(params[key]));\r\n }\r\n return path;\r\n}\r\n\r\n/**\r\n * A React hook for managing the lifecycle of a single API resource (a record).\r\n * It handles fetching, updating, replacing, and deleting a record, while managing\r\n * loading, error, and data states. It supports dynamic path parameters for flexible API routing.\r\n *\r\n * @template T The data type of the record being managed.\r\n * @param {AxiosInstance} axiosInstance - The configured Axios instance for making API calls.\r\n * @param {UseApiRecordConfig<T>} config - Configuration options for the hook.\r\n * @returns {UseApiRecordReturn<T>} An object containing the state, actions, and setState function.\r\n */\r\nexport function useApiRecord<T>(\r\n axiosInstance: AxiosInstance,\r\n config: UseApiRecordConfig<T>\r\n): UseApiRecordReturn<T> {\r\n const {\r\n endpoint,\r\n pathParams,\r\n recordId,\r\n initialData,\r\n enabled = true,\r\n refetchAfterChange = true,\r\n requestConfig,\r\n onSuccess,\r\n onError,\r\n } = config;\r\n\r\n // [STABILITY] Memoize the final endpoint to make it a stable dependency.\r\n // It will only be recalculated if the template or path parameters change.\r\n const finalEndpoint = useMemo(\r\n () => buildDynamicPath(endpoint, pathParams),\r\n [endpoint, pathParams]\r\n );\r\n\r\n // [STABILITY] Memoize initialState to prevent re-creating it on every render,\r\n // which is crucial for preventing infinite loops in hooks that depend on it.\r\n const initialState = useMemo<UseApiRecordState<T>>(() => ({\r\n data: initialData ?? null,\r\n rawResponse: null,\r\n error: null,\r\n loading: enabled && !!recordId,\r\n success: false,\r\n message: undefined,\r\n validationErrors: undefined,\r\n }), [initialData, enabled, recordId]);\r\n\r\n const [state, setState] = useState<UseApiRecordState<T>>(initialState);\r\n\r\n // Use refs for callbacks to prevent stale closures.\r\n const savedOnSuccess = useRef(onSuccess);\r\n const savedOnError = useRef(onError);\r\n\r\n useEffect(() => {\r\n savedOnSuccess.current = onSuccess;\r\n savedOnError.current = onError;\r\n }, [onSuccess, onError]);\r\n\r\n // [STABILITY] Memoize the apiServices instance. It will only be recreated if\r\n // the Axios instance or the final endpoint URL changes.\r\n const apiServices = useMemo(\r\n () => createApiServices<T>(axiosInstance, finalEndpoint),\r\n [axiosInstance, finalEndpoint]\r\n );\r\n\r\n /**\r\n * Fetches the record from the API. This function is memoized and stable.\r\n */\r\n const fetchRecord = useCallback(async () => {\r\n if (!recordId) {\r\n setState(initialState);\r\n return;\r\n }\r\n\r\n setState((prev) => ({ ...prev, loading: true, error: null, success: false }));\r\n\r\n try {\r\n const result = await apiServices.get(recordId, requestConfig);\r\n setState(result);\r\n\r\n if (result.success && savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Record fetched successfully!', result.data ?? undefined);\r\n } else if (!result.success && savedOnError.current) {\r\n savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n }\r\n } catch (e: any) {\r\n console.error('[useApiRecord Fetch Error]', e);\r\n const apiError: ApiError = {\r\n status: e.response?.status || 500,\r\n message: e.message || 'An unexpected client-side error occurred.',\r\n code: e.code,\r\n };\r\n setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n }\r\n }, [apiServices, recordId, requestConfig, initialState]);\r\n\r\n // Effect to trigger the initial data fetch.\r\n useEffect(() => {\r\n if (enabled) {\r\n fetchRecord();\r\n }\r\n }, [enabled, fetchRecord]); // `fetchRecord` is a stable dependency.\r\n\r\n /**\r\n * A centralized handler for all mutation actions (update, put, remove).\r\n */\r\n const handleActionResult = useCallback(async <R extends T | null>(\r\n actionPromise: Promise<StandardResponse<R>>,\r\n options?: ActionOptions\r\n ): Promise<StandardResponse<R>> => {\r\n setState((prev) => ({ ...prev, loading: true }));\r\n\r\n try {\r\n const result = await actionPromise;\r\n\r\n if (result.success) {\r\n if (savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Action successful!', result.data);\r\n }\r\n const shouldRefetch = options?.refetch ?? refetchAfterChange;\r\n if (shouldRefetch) {\r\n await fetchRecord();\r\n } else {\r\n // Optimistic update: set state directly from the action's response.\r\n setState({ ...result, loading: false, data: result.data });\r\n }\r\n } else {\r\n if (savedOnError.current) {\r\n savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n }\r\n setState((prev) => ({ ...prev, ...result, loading: false }));\r\n }\r\n return result;\r\n } catch (e: any) {\r\n console.error('[useApiRecord Action Error]', e);\r\n const apiError: ApiError = {\r\n status: e.response?.status || 500,\r\n message: e.message || 'An unexpected error occurred during the action.',\r\n code: e.code,\r\n };\r\n const errorResponse: StandardResponse<R> = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };\r\n setState(errorResponse as UseApiRecordState<T>);\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n return errorResponse;\r\n }\r\n },\r\n [refetchAfterChange, fetchRecord, initialState]\r\n );\r\n\r\n const actions: UseApiRecordActions<T> = {\r\n fetch: fetchRecord,\r\n update: (updatedItem: Partial<T>, options?: ActionOptions) =>\r\n handleActionResult(apiServices.patch(recordId!, updatedItem, { ...requestConfig, ...options }), options),\r\n put: (item: T, options?: ActionOptions) =>\r\n handleActionResult(apiServices.put(recordId!, item, { ...requestConfig, ...options }), options),\r\n remove: (options?: ActionOptions) =>\r\n handleActionResult(apiServices.remove(recordId!, { ...requestConfig, ...options }), options),\r\n resetState: () => setState(initialState),\r\n };\r\n\r\n return { state, setState, actions };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // file: src/hooks/useApiRecord.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; // <-- أضفنا useMemo\r\n// import { AxiosInstance } from 'axios';\r\n// import { UseApiRecordActions, UseApiRecordConfig, UseApiRecordReturn, UseApiRecordState } from '@/types/useApiRecord.types';\r\n// import { createApiServices } from '@/services/crud';\r\n// import { ActionOptions, ApiError, StandardResponse } from '@/types';\r\n\r\n// /**\r\n// * A React hook for managing the lifecycle of a single API resource (a record).\r\n// * It handles fetching, updating, replacing, and deleting a record, while managing\r\n// * loading, error, and data states.\r\n// */\r\n// export function useApiRecord<T>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiRecordConfig<T>\r\n// ): UseApiRecordReturn<T> {\r\n// const {\r\n// endpoint,\r\n// recordId,\r\n// initialData,\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// // [FIX] Memoize initialState to make it stable across re-renders.\r\n// // It will only be recreated if the initialData prop changes.\r\n// const initialState = useMemo<UseApiRecordState<T>>(() => ({\r\n// data: initialData ?? null,\r\n// rawResponse: null,\r\n// error: null,\r\n// loading: enabled && !!recordId,\r\n// success: false,\r\n// message: undefined,\r\n// validationErrors: undefined,\r\n// }), [initialData, enabled, recordId]); // Dependencies for initialState itself\r\n\r\n// const [state, setState] = useState<UseApiRecordState<T>>(initialState);\r\n\r\n// const savedOnSuccess = useRef(onSuccess);\r\n// const savedOnError = useRef(onError);\r\n\r\n// useEffect(() => {\r\n// savedOnSuccess.current = onSuccess;\r\n// savedOnError.current = onError;\r\n// }, [onSuccess, onError]);\r\n\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// const fetchRecord = useCallback(async () => {\r\n// if (!recordId) {\r\n// // Use the memoized initialState for resetting\r\n// setState(initialState);\r\n// return;\r\n// }\r\n\r\n// setState((prev) => ({ ...prev, loading: true, error: null, success: false }));\r\n\r\n// try {\r\n// const result = await apiServices.get(recordId, requestConfig);\r\n// setState(result);\r\n\r\n// if (result.success && savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Record fetched successfully!', result.data ?? undefined);\r\n// } else if (!result.success && savedOnError.current) {\r\n// savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// } catch (e: any) {\r\n// console.error('[useApiRecord Fetch Error]', e);\r\n// const apiError: ApiError = {\r\n// status: e.response?.status || 500,\r\n// message: e.message || 'An unexpected client-side error occurred.',\r\n// code: e.code,\r\n// };\r\n// setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// }\r\n// // [FIX] Removed unstable `initialState` from dependency array.\r\n// // Also, `requestConfig` can be unstable if defined as an object literal in the parent component.\r\n// // Advise users to memoize `requestConfig` if it's complex.\r\n// }, [apiServices, recordId, requestConfig, initialState]); // initialState is now stable due to useMemo\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchRecord();\r\n// }\r\n// }, [enabled, fetchRecord]); // Removed recordId as it's already a dependency of fetchRecord\r\n\r\n// const handleActionResult = useCallback(\r\n// async <R extends T | null>(\r\n// actionPromise: Promise<StandardResponse<R>>,\r\n// options?: ActionOptions\r\n// ): Promise<StandardResponse<R>> => {\r\n// setState((prev) => ({ ...prev, loading: true }));\r\n\r\n// try {\r\n// const result = await actionPromise;\r\n\r\n// if (result.success) {\r\n// if (savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Action successful!', result.data);\r\n// }\r\n// const shouldRefetch = options?.refetch ?? refetchAfterChange;\r\n// if (shouldRefetch) {\r\n// await fetchRecord();\r\n// } else {\r\n// setState({ ...result, loading: false, data: result.data });\r\n// }\r\n// } else {\r\n// if (savedOnError.current) {\r\n// savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n// setState((prev) => ({ ...prev, ...result, loading: false }));\r\n// }\r\n// return result;\r\n// } catch (e: any) {\r\n// console.error('[useApiRecord Action Error]', e);\r\n// const apiError: ApiError = {\r\n// status: e.response?.status || 500,\r\n// message: e.message || 'An unexpected error occurred during the action.',\r\n// code: e.code,\r\n// };\r\n// // Use the stable initialState for the error response structure\r\n// const errorResponse: StandardResponse<R> = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };\r\n// setState(errorResponse as UseApiRecordState<T>);\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// return errorResponse;\r\n// }\r\n// },\r\n// // [FIX] Removed unstable dependencies. `fetchRecord` is now stable.\r\n// [refetchAfterChange, fetchRecord, initialState]\r\n// );\r\n\r\n// const actions: UseApiRecordActions<T> = {\r\n// fetch: fetchRecord,\r\n// update: (updatedItem: Partial<T>, options?: ActionOptions) =>\r\n// handleActionResult(apiServices.patch(recordId!, updatedItem, { ...requestConfig, ...options }), options),\r\n// put: (item: T, options?: ActionOptions) =>\r\n// handleActionResult(apiServices.put(recordId!, item, { ...requestConfig, ...options }), options),\r\n// remove: (options?: ActionOptions) =>\r\n// handleActionResult(apiServices.remove(recordId!, { ...requestConfig, ...options }), options),\r\n// resetState: () => setState(initialState),\r\n// };\r\n\r\n// return { state, setState, actions };\r\n// }","import { useEffect, useRef } from 'react';\r\nimport isEqual from 'fast-deep-equal';\r\n\r\ntype EffectCallback = () => (void | (() => void));\r\ntype DependencyList = readonly any[];\r\n\r\nexport function useDeepCompareEffect(callback: EffectCallback, dependencies: DependencyList) {\r\n const currentDependenciesRef = useRef<DependencyList>();\r\n\r\n if (!isEqual(currentDependenciesRef.current, dependencies)) {\r\n currentDependenciesRef.current = dependencies;\r\n }\r\n\r\n useEffect(callback, [currentDependenciesRef.current]);\r\n}","// file: src/hooks/useApiModule/useApiModule.ts\r\n\r\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { globalStateManager } from '../../core/globalStateManager';\r\nimport { callDynamicApi } from '../../services/crud';\r\nimport { \r\n ApiModuleConfig, UseApiModuleOptions, UseApiModuleReturn, ModuleStates, ModuleActions,\r\n ExecuteOptions, InputOf, OutputOf, ActionConfigModule, ActionState\r\n} from '../../types/apiModule.types';\r\nimport { QueryOptions, StandardResponse, UseApiQuery } from '../../types';\r\n\r\nconst ApiModuleContext = createContext<UseApiModuleReturn<any> | null>(null);\r\nexport const ApiModuleProvider = ApiModuleContext.Provider;\r\n\r\n\r\n\r\nexport function useModuleContext<TModule extends ApiModuleConfig<any>>(): UseApiModuleReturn<TModule['actions']> {\r\n const context = useContext(ApiModuleContext);\r\n if (!context) {\r\n throw new Error('useModuleContext must be used within an ApiModuleProvider');\r\n }\r\n // نحن نقوم بعمل cast آمن هنا، لأننا نثق أن المطور سيستخدم الـ Provider الصحيح\r\n return context as UseApiModuleReturn<TModule['actions']>;\r\n}\r\n\r\nconst generateCacheKey = (\r\n moduleName: string,\r\n actionName: string,\r\n input?: unknown,\r\n callOptions: { pathParams?: Record<string, any> } = {}\r\n): string => {\r\n const params = { path: callOptions.pathParams, body: input };\r\n try {\r\n return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n } catch (error) {\r\n return `${moduleName}/${actionName}::${Date.now()}`;\r\n }\r\n};\r\n\r\nexport function useApiModule<TActions extends Record<string, ActionConfigModule<any, any>>>(\r\n axiosInstance: AxiosInstance,\r\n moduleConfig: ApiModuleConfig<TActions>,\r\n options: UseApiModuleOptions = {}\r\n): UseApiModuleReturn<TActions> {\r\n const { refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams, enabled = true, hydratedState } = options;\r\n\r\n const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n const savedCallbacks = useRef({ onSuccess, onError });\r\n useEffect(() => { savedCallbacks.current = { onSuccess, onError }; }, [onSuccess, onError]);\r\n\r\n useMemo(() => {\r\n if (hydratedState) {\r\n globalStateManager.rehydrate(hydratedState);\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [hydratedState]);\r\n\r\n const actions = useMemo<ModuleActions<TActions>>(() => {\r\n return (Object.keys(moduleConfig.actions) as Array<keyof TActions>).reduce((acc, actionName) => {\r\n const actionConfig = moduleConfig.actions[actionName];\r\n \r\n const execute = async <TContext = unknown>(\r\n input?: InputOf<typeof actionConfig>,\r\n options: ExecuteOptions<InputOf<typeof actionConfig>, OutputOf<typeof actionConfig>, TContext> = {}\r\n ): Promise<StandardResponse<OutputOf<typeof actionConfig>>> => {\r\n const finalPathParams = { ...(JSON.parse(pathParamsString)), ...options.pathParams };\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams: finalPathParams });\r\n let mutationContext: TContext | undefined;\r\n\r\n try {\r\n if (options.onMutate) {\r\n mutationContext = await options.onMutate(input as InputOf<typeof actionConfig>);\r\n }\r\n\r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, loading: true, called: true, error: null, isStale: false }));\r\n \r\n const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n pathParams: finalPathParams, body: input, config: options.config,\r\n });\r\n \r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...result, loading: false }));\r\n \r\n if (result.success) {\r\n savedCallbacks.current.onSuccess?.(actionName as string, result.message || 'Action successful', result.data);\r\n options.onSuccess?.(result.data, mutationContext);\r\n \r\n actionConfig.invalidates?.forEach((keyToInvalidate: string) => {\r\n const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;\r\n globalStateManager.invalidateByPrefix(prefix);\r\n });\r\n } else {\r\n savedCallbacks.current.onError?.(actionName as string, result.message || 'Action failed', result.error ?? undefined);\r\n options.onError?.(result.error!, mutationContext);\r\n }\r\n return result;\r\n } catch (error: any) {\r\n const apiError = { status: 500, message: error.message };\r\n const errorResult = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...errorResult, loading: false }));\r\n \r\n savedCallbacks.current.onError?.(actionName as string, apiError.message, apiError);\r\n options.onError?.(apiError, mutationContext);\r\n return errorResult;\r\n }\r\n };\r\n\r\n const reset = (input?: InputOf<typeof actionConfig>, options: { pathParams?: Record<string, any> } = {}) => {\r\n const finalPathParams = { ...(JSON.parse(pathParamsString)), ...options.pathParams };\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams: finalPathParams });\r\n globalStateManager.setState(cacheKey, () => ({\r\n data: null, error: null, loading: false, success: false,\r\n called: false, isStale: false, rawResponse: null\r\n }));\r\n };\r\n\r\n acc[actionName] = { execute, reset };\r\n return acc;\r\n }, {} as ModuleActions<TActions>);\r\n }, [axiosInstance, moduleConfig, pathParamsString]);\r\n\r\n const queries = useMemo(() => {\r\n const builtQueries: any = {};\r\n for (const actionName in moduleConfig.actions) {\r\n if (moduleConfig.actions[actionName]?.hasQuery) {\r\n const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n };\r\n builtQueries[actionName] = {\r\n options: queryOptions[actionName] || {}, setOptions: setActionQueryOptions,\r\n setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n setFilters: (filter: Record<string, unknown>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n setQueryParam: (key: string, value: unknown) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? value : p.page })),\r\n reset: () => setActionQueryOptions({}),\r\n };\r\n }\r\n }\r\n return builtQueries as UseApiModuleReturn<TActions>['queries'];\r\n }, [queryOptions, moduleConfig.actions]);\r\n \r\n const states = useMemo<ModuleStates<TActions>>(() => {\r\n const finalStates: any = {};\r\n for (const actionName of Object.keys(moduleConfig.actions) as Array<keyof TActions>) {\r\n Object.defineProperty(finalStates, actionName, {\r\n get: () => {\r\n const actionConfig = moduleConfig.actions[actionName];\r\n const query = queries[actionName as keyof typeof queries]?.options;\r\n const input: unknown = actionConfig.hasQuery ? query : undefined;\r\n \r\n const pathParams = JSON.parse(pathParamsString);\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams });\r\n \r\n const state = useSyncExternalStore(\r\n (callback) => globalStateManager.subscribe(cacheKey, callback),\r\n () => globalStateManager.getSnapshot<ActionState<OutputOf<typeof actionConfig>>>(cacheKey)\r\n );\r\n\r\n useEffect(() => {\r\n if (enabled && state.isStale && !state.loading) {\r\n actions[actionName].execute(input as never);\r\n }\r\n }, [enabled, state.isStale, state.loading, input, actions, actionName]);\r\n \r\n return state;\r\n },\r\n enumerable: true,\r\n });\r\n }\r\n return finalStates as ModuleStates<TActions>;\r\n }, [moduleConfig, pathParamsString, queries, enabled, actions]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n const actionKeys = Object.keys(moduleConfig.actions) as Array<keyof TActions>;\r\n for (const actionName of actionKeys) {\r\n const state = states[actionName];\r\n const actionConfig = moduleConfig.actions[actionName];\r\n if (actionConfig.autoFetch && state && !state.called && !state.loading) {\r\n const query = queries[actionName as keyof typeof queries]?.options;\r\n const input: unknown = actionConfig.hasQuery ? query : undefined;\r\n actions[actionName].execute(input as never);\r\n }\r\n }\r\n }, [enabled, moduleConfig, actions, states, queries]);\r\n\r\n const lastBlurTimestamp = useRef(Date.now());\r\n useEffect(() => {\r\n if (!enabled || !refetchOnWindowFocus) return;\r\n const onFocus = () => {\r\n if (Date.now() - lastBlurTimestamp.current > 10000) {\r\n const actionKeys = Object.keys(moduleConfig.actions) as Array<keyof TActions>;\r\n for (const actionName of actionKeys) {\r\n const state = states[actionName];\r\n if (state && state.called && !state.loading) {\r\n const prefix = `${moduleConfig.baseEndpoint}/${actionName as string}::`;\r\n globalStateManager.invalidateByPrefix(prefix);\r\n }\r\n }\r\n }\r\n };\r\n const onBlur = () => { lastBlurTimestamp.current = Date.now(); };\r\n\r\n window.addEventListener('focus', onFocus);\r\n window.addEventListener('blur', onBlur);\r\n return () => {\r\n window.removeEventListener('focus', onFocus);\r\n window.removeEventListener('blur', onBlur);\r\n };\r\n }, [enabled, refetchOnWindowFocus, moduleConfig, states]);\r\n\r\n const dehydrate = useMemo(() => {\r\n return () => globalStateManager.dehydrate();\r\n }, []);\r\n\r\n return { actions, states, queries, dehydrate };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useMemo, useEffect, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { StandardResponse, QueryOptions, ApiError, UseApiQuery, ModuleActions, UseApiModuleOptions, ActionState, ApiModuleConfig, ActionConfigModule } from '../../types';\r\n// import { cacheManager } from '../../core/cache';\r\n// import { callDynamicApi } from '../../services/crud';\r\n\r\n\r\n\r\n// const generateCacheKey = (\r\n// moduleName: string,\r\n// actionName: string,\r\n// input?: any,\r\n// callOptions: { pathParams?: Record<string, any>; query?: QueryOptions } = {}\r\n// ): string => {\r\n// // تم تبسيط هذا الجزء ليكون أكثر أمانًا\r\n// const params = {\r\n// path: callOptions.pathParams,\r\n// body: input,\r\n// query: callOptions.query,\r\n// };\r\n// try {\r\n// return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n// } catch (error) {\r\n// console.error(\"Could not stringify API params for cache key:\", error);\r\n// return `${moduleName}/${actionName}::${Date.now()}`;\r\n// }\r\n// };\r\n\r\n// export function useApiModule<TModule extends ApiModuleConfig>(\r\n// axiosInstance: AxiosInstance,\r\n// moduleConfig: TModule,\r\n// options: UseApiModuleOptions = {}\r\n// ): any {\r\n// const { staleTime = 0, cacheTime, refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams } = options;\r\n\r\n// const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n\r\n// const [states, setStates] = useState<Record<keyof TModule['actions'], ActionState<any>>>(() => {\r\n// const initialStates: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// initialStates[actionName] = {\r\n// data: null, links: undefined, meta: undefined, error: null,\r\n// loading: false, success: false, called: false, message: undefined,\r\n// validationErrors: [], rawResponse: null,\r\n// };\r\n// }\r\n// return initialStates as Record<keyof TModule['actions'], ActionState<any>>;\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n// const savedCallbacks = useRef({ onSuccess, onError });\r\n// useEffect(() => {\r\n// savedCallbacks.current = { onSuccess, onError };\r\n// }, [onSuccess, onError]);\r\n\r\n// const actions = useMemo(() => {\r\n// const builtActions: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// const actionConfig = moduleConfig.actions[actionName] as ActionConfigModule<any, any>;\r\n \r\n// const execute = async (\r\n// input?: any,\r\n// callOptions: { pathParams?: any; config?: any; query?: any } = {}\r\n// ): Promise<StandardResponse<any>> => {\r\n// const updateState = (partialState: Partial<ActionState<any>>) => {\r\n// setStates(prev => ({ ...prev, [actionName]: { ...(prev[actionName] as object), ...partialState } }));\r\n// };\r\n \r\n// const finalPathParams = { ...(JSON.parse(pathParamsString)), ...callOptions.pathParams };\r\n// const finalCallOptions = { ...callOptions, pathParams: finalPathParams };\r\n\r\n// const isGetRequest = actionConfig.method === 'GET';\r\n// if (isGetRequest) {\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n \r\n// // [FIX 1] نحدد النوع المتوقع من الكاش وهو StandardResponse<any>\r\n// const cachedEntry = cacheManager.getWithMeta<StandardResponse<any>>(cacheKey);\r\n\r\n// // [FIX 2] الآن cachedEntry هو إما null أو CacheItem<StandardResponse<any>>\r\n// if (cachedEntry) {\r\n// const isStale = Date.now() - cachedEntry.timestamp > staleTime;\r\n\r\n// if (!isStale) {\r\n// // الآن cachedEntry.data هو من نوع StandardResponse<any>، وليس unknown\r\n// const cachedState: ActionState<any> = {\r\n// ...cachedEntry.data, // لا توجد مشكلة الآن\r\n// loading: false,\r\n// called: true,\r\n// };\r\n// updateState(cachedState);\r\n// return cachedEntry.data; // لا توجد مشكلة الآن\r\n// }\r\n// // البيانات قديمة، اعرضها وقم بالتحديث في الخلفية\r\n// updateState({ ...cachedEntry.data, loading: true, called: true }); // لا توجد مشكلة الآن\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n\r\n// try {\r\n// // باقي الكود يبقى كما هو لأنه صحيح\r\n// const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n// pathParams: finalPathParams,\r\n// body: input,\r\n// config: callOptions.config,\r\n// });\r\n \r\n// updateState({ ...result, loading: false });\r\n \r\n// if (result.success) {\r\n// // [FIX 3] عند تخزين البيانات في الكاش، يجب تخزين الكائن result الكامل\r\n// if (isGetRequest) {\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n// cacheManager.set(cacheKey, result, cacheTime);\r\n// }\r\n// savedCallbacks.current.onSuccess?.(actionName, result.message || 'Action successful', result.data);\r\n// } else {\r\n// savedCallbacks.current.onError?.(actionName, result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n \r\n// return result;\r\n// } catch (error: any) {\r\n// const apiError: ApiError = { status: 500, message: error.message || \"An unexpected error occurred.\" };\r\n// const errorResult: StandardResponse<any> = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n// updateState({ ...errorResult, loading: false });\r\n \r\n// savedCallbacks.current.onError?.(actionName, apiError.message, apiError);\r\n// return errorResult;\r\n// }\r\n// };\r\n// const reset = () => {\r\n// setStates(prev => ({ ...prev, [actionName]: {\r\n// data: null, links: undefined, meta: undefined, error: null,\r\n// loading: false, success: false, called: false, message: undefined,\r\n// validationErrors: [], rawResponse: null,\r\n// }}));\r\n// };\r\n\r\n// builtActions[actionName] = { execute, reset };\r\n// }\r\n// return builtActions as ModuleActions<TModule>;\r\n// }, [axiosInstance, moduleConfig, staleTime, cacheTime, pathParamsString]);\r\n\r\n // const queries = useMemo(() => {\r\n // const builtQueries: Record<string, UseApiQuery | undefined> = {};\r\n // for (const actionName in moduleConfig.actions) {\r\n // if (moduleConfig.actions[actionName]?.isList) {\r\n // const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n // setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n // };\r\n // builtQueries[actionName] = {\r\n // options: queryOptions[actionName] || {},\r\n // setOptions: setActionQueryOptions,\r\n // setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n // setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n // setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n // setFilters: (filter: Record<string, any>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n // setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n // setQueryParam: (key: string, value: any) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? 1 : value })),\r\n // reset: () => setActionQueryOptions({}),\r\n // };\r\n // }\r\n // }\r\n // return builtQueries;\r\n // }, [queryOptions, moduleConfig.actions]);\r\n\r\n// useEffect(() => {\r\n// if (!refetchOnWindowFocus) return;\r\n// const onFocus = () => {\r\n// // يمكنك هنا إضافة منطق إعادة الجلب عند التركيز على النافذة\r\n// };\r\n// window.addEventListener('focus', onFocus);\r\n// return () => window.removeEventListener('focus', onFocus);\r\n// }, [refetchOnWindowFocus, actions]);\r\n\r\n// return {\r\n// actions,\r\n// states,\r\n// queries,\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useMemo, useEffect, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import {\r\n// ApiModuleConfig,\r\n// ModuleActions,\r\n// ActionState,\r\n// ActionConfig,\r\n// UseApiModuleOptions,\r\n// } from '../../types/apiModule.types'; \r\n// import { StandardResponse, QueryOptions, ApiError } from '../../types'; \r\n// import { cacheManager } from '@/core/cache';\r\n// import { callDynamicApi } from '@/services/crud';\r\n// import { UseApiQuery } from '@/types/useApi.types';\r\n\r\n// const generateCacheKey = (\r\n// moduleName: string,\r\n// actionName: string,\r\n// input?: any,\r\n// callOptions: { pathParams?: Record<string, any>; query?: QueryOptions } = {}\r\n// ): string => {\r\n// const params = { path: callOptions.pathParams, body: input, query: callOptions.query };\r\n// try {\r\n// return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n// } catch (error) {\r\n// console.error(\"Could not stringify API params for cache key:\", error);\r\n// return `${moduleName}/${actionName}::${Date.now()}`;\r\n// }\r\n// };\r\n\r\n\r\n// export function useApiModule<TModule extends ApiModuleConfig>(\r\n// axiosInstance: AxiosInstance,\r\n// moduleConfig: TModule,\r\n// options: UseApiModuleOptions = {}\r\n// ):any {\r\n// const { staleTime = 0, cacheTime, refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams } = options;\r\n\r\n// const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n\r\n// const [states, setStates] = useState<Record<keyof TModule['actions'], ActionState<any>>>(() => {\r\n// const initialStates: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// initialStates[actionName] = { data: null, error: null, loading: false, success: false, called: false, response: null };\r\n// }\r\n// return initialStates as Record<keyof TModule['actions'], ActionState<any>>;\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n// const savedCallbacks = useRef({ onSuccess, onError });\r\n// useEffect(() => {\r\n// savedCallbacks.current = { onSuccess, onError };\r\n// }, [onSuccess, onError]);\r\n\r\n// const actions = useMemo(() => {\r\n// const builtActions: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// const actionConfig = moduleConfig.actions[actionName] as ActionConfig<any, any>;\r\n \r\n// const execute = async (\r\n// input?: any,\r\n// callOptions: { pathParams?: any; config?: any; query?: any } = {}\r\n// ): Promise<StandardResponse<any>> => {\r\n// const updateState = (partialState: Partial<ActionState<any>>) => {\r\n// setStates(prev => ({ ...prev, [actionName]: { ...(prev[actionName]), ...partialState } }));\r\n// };\r\n \r\n// const finalPathParams = { ...(JSON.parse(pathParamsString)), ...callOptions.pathParams };\r\n// const finalCallOptions = { ...callOptions, pathParams: finalPathParams };\r\n\r\n// const isGetRequest = actionConfig.method === 'GET';\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n \r\n// if (isGetRequest) {\r\n// const cachedEntry = cacheManager.getWithMeta(cacheKey);\r\n// if (cachedEntry) {\r\n// const isStale = Date.now() - cachedEntry.timestamp > staleTime;\r\n// if (!isStale) {\r\n// const result: StandardResponse<any> = { data: cachedEntry.data, success: true, loading: false, error: null, rawResponse: undefined };\r\n// updateState({ ...result, called: true, response: result });\r\n// return result;\r\n// }\r\n// updateState({ data: cachedEntry.data, loading: true, error: null, called: true });\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n\r\n// try {\r\n// const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n// pathParams: finalPathParams,\r\n// body: input,\r\n// config: callOptions.config,\r\n// });\r\n \r\n// if (result.success) {\r\n// savedCallbacks.current.onSuccess?.(actionName, result.message || 'Action successful', result.data);\r\n// } else {\r\n// savedCallbacks.current.onError?.(actionName, result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n \r\n// updateState({ loading: false, data: result.data, error: result.error, success: result.success, response: result });\r\n// return result;\r\n// } catch (error: any) {\r\n// const apiError: ApiError = { status: 500, message: error.message || \"An unexpected error occurred.\" };\r\n// const errorResult: StandardResponse<any> = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n// updateState({ ...errorResult, response: errorResult });\r\n \r\n// savedCallbacks.current.onError?.(actionName, apiError.message, apiError);\r\n// return errorResult;\r\n// }\r\n// };\r\n// const reset = () => {\r\n// setStates(prev => ({ ...prev, [actionName]: { data: null, error: null, loading: false, success: false, called: false, response: null } }));\r\n// };\r\n\r\n// builtActions[actionName] = { execute, reset };\r\n// }\r\n// return builtActions as ModuleActions<TModule>;\r\n// }, [axiosInstance, moduleConfig, staleTime, cacheTime, pathParamsString]);\r\n\r\n// const queries = useMemo(() => {\r\n// const builtQueries: Record<string, UseApiQuery | undefined> = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// if (moduleConfig.actions[actionName]?.isList) {\r\n// const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n// setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n// };\r\n// builtQueries[actionName] = {\r\n// options: queryOptions[actionName] || {},\r\n// setOptions: setActionQueryOptions,\r\n// setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n// setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n// setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n// setFilters: (filter: Record<string, any>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n// setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n// setQueryParam: (key: string, value: any) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? 1 : value })),\r\n// reset: () => setActionQueryOptions({}),\r\n// };\r\n// }\r\n// }\r\n// return builtQueries;\r\n// }, [queryOptions, moduleConfig.actions]);\r\n\r\n\r\n\r\n\r\n// useEffect(() => {\r\n// if (!refetchOnWindowFocus) return;\r\n// const onFocus = () => {\r\n// console.log(\"Window focused. Consider implementing refetch logic.\");\r\n// };\r\n// window.addEventListener('focus', onFocus);\r\n// return () => window.removeEventListener('focus', onFocus);\r\n// }, [refetchOnWindowFocus, actions]);\r\n\r\n\r\n// return {\r\n// actions,\r\n// states,\r\n// queries,\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"],"mappings":"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","names":["axios","uuidv4","import_axios","import_axios","axios","axios","import_react","buildDynamicPath","import_react","isEqual","import_react","options"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/client.ts","../src/core/processor.ts","../src/core/utils.ts","../src/core/buildDynamicUrl.ts","../src/services/crud.ts","../src/services/actions.ts","../src/core/globalStateManager.ts","../src/core/cache.ts","../src/hooks/useApi.ts","../src/hooks/useApiRecord/index.ts","../src/hooks/useDeepCompareEffect/index.ts","../src/hooks/useApiModule/useApiModule.ts"],"sourcesContent":["// file: api-core-lib/src/index.ts\r\n\r\n/**\r\n * ===================================================================================\r\n * CLIENT-SIDE ENTRY POINT\r\n * ===================================================================================\r\n * This file exports all utilities, hooks, and types needed for a client-side\r\n * React application (components marked with \"use client\").\r\n * It MUST NOT export server-only utilities.\r\n */\r\n\r\n// --- Core utilities safe for both environments ---\r\nexport { createApiClient } from './core/client';\r\nexport { callDynamicApi, createApiServices } from './services/crud';\r\nexport { createApiActions } from './services/actions';\r\nexport { globalStateManager } from './core/globalStateManager';\r\nexport { buildPaginateQuery } from './core/utils';\r\nexport { processResponse } from './core/processor';\r\n\r\n// [NOTE] Consolidating state managers. \r\n// It's recommended to use 'globalStateManager' from the new architecture.\r\n// 'cacheManager' is exported for legacy compatibility if needed.\r\nexport { cacheManager } from './core/cache';\r\n\r\n// --- Hooks (Client components ONLY) ---\r\nexport { useApi } from './hooks/useApi';\r\nexport { useApiRecord } from './hooks/useApiRecord';\r\nexport { useDeepCompareEffect } from './hooks/useDeepCompareEffect';\r\n\r\n// The primary hook for the new architecture\r\nexport { useApiModule, useModuleContext, ApiModuleProvider } from './hooks/useApiModule/useApiModule';\r\n\r\n// --- Types (safe to export everywhere) ---\r\nexport * from './types';\r\nexport * from './types/apiModule.types';\r\nexport * from './types/useApi.types';\r\nexport * from './types/useApiResource.types';\r\nexport * from './types/useApiRecord.types';","import axios, {\r\n AxiosInstance,\r\n AxiosError,\r\n AxiosResponse,\r\n InternalAxiosRequestConfig,\r\n} from 'axios';\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport {\r\n ApiClientConfig,\r\n Middleware,\r\n MiddlewareContext,\r\n ApiError,\r\n RequestConfig,\r\n Tokens,\r\n TokenManager,\r\n} from '../types';\r\n\r\n// ملاحظة هامة: يجب عليك تحديث تعريف `MiddlewareContext` في ملف `types.ts`\r\n// وحذف خاصية `logger` منه.\r\n\r\nasync function runMiddleware(\r\n context: MiddlewareContext,\r\n middlewares: Middleware[] = [],\r\n): Promise<void> {\r\n const run = async (index: number): Promise<void> => {\r\n if (index >= middlewares.length) return;\r\n const middleware = middlewares[index];\r\n await middleware(context, () => run(index + 1));\r\n };\r\n await run(0);\r\n}\r\n\r\n// تم حذف معامل `logger` من الدالة\r\nasync function refreshToken(\r\n config: ApiClientConfig,\r\n tokenManager: TokenManager,\r\n): Promise<Tokens | null> {\r\n const { refreshTokenConfig } = config;\r\n\r\n if (!refreshTokenConfig) {\r\n console.warn('Token refresh is disabled: `refreshTokenConfig` is not provided.');\r\n await tokenManager.clearTokens();\r\n return null;\r\n }\r\n\r\n try {\r\n const currentTokens = await tokenManager.getTokens();\r\n if (!currentTokens.refreshToken) {\r\n throw new Error(\"No refresh token available to perform refresh.\");\r\n }\r\n\r\n const { path, buildRequestBody, buildRequestHeaders, extractTokens } = refreshTokenConfig;\r\n \r\n const requestBody = buildRequestBody \r\n ? buildRequestBody(currentTokens.refreshToken) \r\n : { refresh_token: currentTokens.refreshToken }; \r\n\r\n const requestHeaders = buildRequestHeaders \r\n ? buildRequestHeaders(currentTokens) \r\n : {};\r\n \r\n const response = await axios.post(\r\n `${config.baseURL}${path}`, \r\n requestBody,\r\n { headers: requestHeaders, withCredentials: config.withCredentials }\r\n );\r\n\r\n const extracted = extractTokens(response.data);\r\n if (!extracted || !extracted.accessToken || typeof extracted.expiresIn !== 'number') {\r\n throw new Error(\"extractTokens function failed to return a valid token structure.\");\r\n }\r\n \r\n const newTokens: Tokens = {\r\n accessToken: extracted.accessToken,\r\n refreshToken: extracted.refreshToken || currentTokens.refreshToken,\r\n expiresAt: Date.now() + extracted.expiresIn * 1000,\r\n tokenType: extracted.tokenType || 'Bearer',\r\n };\r\n\r\n await tokenManager.setTokens(newTokens);\r\n console.info('Tokens refreshed successfully.');\r\n return newTokens;\r\n\r\n } catch (err: any) {\r\n console.error('Failed to refresh token.', err.response?.data || err.message);\r\n if (config.onRefreshError) {\r\n config.onRefreshError(err);\r\n }\r\n await tokenManager.clearTokens();\r\n return null;\r\n }\r\n}\r\n\r\nexport function createApiClient(config: ApiClientConfig): AxiosInstance {\r\n const {\r\n baseURL,\r\n tokenManager,\r\n timeout = 15000,\r\n headers = {},\r\n withCredentials = false,\r\n middleware = [],\r\n defaultIsPublic = false,\r\n maxTokenRefreshRetries = 2,\r\n maxQueueSize = 50,\r\n onRefreshError\r\n } = config;\r\n \r\n // لا يوجد متغير `logger`، سيتم استخدام `console` مباشرة\r\n\r\n const axiosInstance = axios.create({\r\n baseURL,\r\n timeout,\r\n headers: { 'Content-Type': 'application/json', ...headers },\r\n withCredentials,\r\n });\r\n\r\n let isRefreshing = false;\r\n let refreshAttempts = 0;\r\n let failedQueue: { resolve: (value: any) => void; reject: (reason?: any) => void }[] = [];\r\n\r\n const processQueue = (error: any, token: string | null = null) => {\r\n failedQueue.forEach(prom => (error ? prom.reject(error) : prom.resolve(token)));\r\n failedQueue = [];\r\n };\r\n\r\n const safeRunMiddleware = async (context: MiddlewareContext, middlewareList?: Middleware[]) => {\r\n try {\r\n await runMiddleware(context, middlewareList);\r\n } catch (err) {\r\n console.error('Middleware Error', err);\r\n }\r\n };\r\n\r\n axiosInstance.interceptors.request.use(async (req: InternalAxiosRequestConfig & RequestConfig) => {\r\n req.headers['X-Request-ID'] = uuidv4();\r\n try {\r\n const fullUrl = axios.getUri({ ...req, baseURL: req.baseURL ?? baseURL });\r\n console.log(`Request Sent > ${req.method?.toUpperCase()} ${fullUrl}`, `(ID: ${req.headers['X-Request-ID']})`);\r\n } catch {\r\n console.warn('Failed to build full request URL for logging.');\r\n }\r\n\r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req, custom: {} }, middleware);\r\n\r\n if (req.isPublic ?? defaultIsPublic) {\r\n return req;\r\n }\r\n\r\n if (tokenManager.isHttpOnly()) return req;\r\n\r\n let tokens = await tokenManager.getTokens();\r\n const now = Date.now();\r\n const tokenBuffer = 60 * 1000;\r\n\r\n if (tokens.accessToken && tokens.expiresAt && (tokens.expiresAt - now < tokenBuffer) && !isRefreshing) {\r\n if (config.refreshTokenConfig) {\r\n console.info('Proactive token refresh initiated.');\r\n isRefreshing = true;\r\n try {\r\n const newTokens = await refreshToken(config, tokenManager);\r\n if (newTokens) tokens = newTokens;\r\n } catch (err) {\r\n console.error('Proactive refresh failed', err);\r\n } finally {\r\n isRefreshing = false;\r\n }\r\n }\r\n }\r\n\r\n if (tokens.accessToken) {\r\n req.headers.Authorization = `${tokens.tokenType || 'Bearer'} ${tokens.accessToken}`;\r\n }\r\n\r\n return req;\r\n });\r\n\r\n axiosInstance.interceptors.response.use(\r\n async (res) => {\r\n try {\r\n const fullUrl = axios.getUri(res.config);\r\n console.log(`Response Received < ${res.config.method?.toUpperCase()} ${fullUrl}`, `(ID: ${res.config.headers['X-Request-ID']}, Status: ${res.status})`);\r\n } catch {\r\n console.warn('Failed to build full response URL for logging.');\r\n }\r\n \r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req: res.config, res, custom: {} }, middleware);\r\n return res;\r\n },\r\n async (error: AxiosError) => {\r\n const originalRequest = error.config as any;\r\n\r\n // [الحل الرئيسي] إعادة بناء منطق تسجيل الأخطاء بالكامل\r\n try {\r\n const fullUrl = axios.getUri(originalRequest);\r\n const method = originalRequest.method?.toUpperCase();\r\n const status = error.response?.status;\r\n const responseData = error.response?.data as { message?: string } | undefined;\r\n\r\n // 1. بناء رسالة خطأ موجزة ومفيدة\r\n const summary = `Response Error < ${method} ${fullUrl} | Status: ${status || 'N/A'}`;\r\n \r\n // 2. استخراج رسالة الخطأ من ה-API إن وجدت\r\n const apiMessage = responseData?.message || error.message;\r\n\r\n // 3. طباعة الرسالة الموجزة ورسالة ה-API\r\n console.error(summary);\r\n console.error(`> Message: ${apiMessage}`);\r\n\r\n // 4. [اختياري ولكن موصى به] طباعة كائن الاستجابة الكامل بشكل قابل للفحص\r\n if (error.response) {\r\n console.groupCollapsed('Full Error Response Details');\r\n console.dir(error.response.data);\r\n console.groupEnd();\r\n }\r\n \r\n } catch {\r\n console.error('Response Error: Failed to build full URL for logging.', error.message);\r\n }\r\n\r\n \r\n // تم حذف خاصية `logger` من الكائن\r\n await safeRunMiddleware({ req: originalRequest, error, custom: {} }, middleware);\r\n\r\n const isHttpOnlyMode = tokenManager.isHttpOnly();\r\n const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;\r\n\r\n if (isHttpOnlyMode && canAttemptRefresh) {\r\n console.error(\"401 in httpOnly mode. Token refresh must be handled by the server.\");\r\n return Promise.reject(error);\r\n }\r\n\r\n if (!isHttpOnlyMode && canAttemptRefresh) {\r\n if (isRefreshing) {\r\n if (failedQueue.length >= maxQueueSize) {\r\n console.warn('Failed request queue overflow, rejecting request.');\r\n return Promise.reject(error);\r\n }\r\n return new Promise((resolve, reject) => failedQueue.push({ resolve, reject }))\r\n .then(token => {\r\n originalRequest.headers['Authorization'] = `Bearer ${token}`;\r\n return axiosInstance(originalRequest);\r\n });\r\n }\r\n\r\n if (refreshAttempts >= maxTokenRefreshRetries) {\r\n console.error('Max token refresh attempts reached.');\r\n return Promise.reject(error);\r\n }\r\n\r\n refreshAttempts++;\r\n originalRequest._retry = true;\r\n isRefreshing = true;\r\n\r\n try {\r\n // استدعاء الدالة بدون `logger`\r\n const newTokens = await refreshToken(config, tokenManager);\r\n if (!newTokens?.accessToken) {\r\n throw new Error('Token refresh failed to produce a new access token.');\r\n }\r\n processQueue(null, newTokens.accessToken);\r\n originalRequest.headers['Authorization'] = `${newTokens.tokenType || 'Bearer'} ${newTokens.accessToken}`;\r\n return axiosInstance(originalRequest);\r\n } catch (refreshError) {\r\n processQueue(refreshError, null);\r\n return Promise.reject(refreshError);\r\n } finally {\r\n isRefreshing = false;\r\n }\r\n }\r\n\r\n return Promise.reject(error);\r\n }\r\n );\r\n\r\n return axiosInstance;\r\n}\r\n\r\n\r\n// v1\r\n// export function createApiClient(config: ApiClientConfig): AxiosInstance {\r\n// const {\r\n// baseURL,\r\n// tokenManager,\r\n// timeout = 15000,\r\n// headers = {},\r\n// withCredentials = false,\r\n// middleware = [],\r\n// logger: externalLogger = console,\r\n// // ✅ NEW: logLevel is now an optional parameter with a default value.\r\n// logLevel = 'info',\r\n// defaultIsPublic = false,\r\n// } = config;\r\n\r\n// // --- Logger Wrapper for Log Level Control ---\r\n// const LOG_LEVELS: Record<LogLevel, number> = { 'debug': 4, 'info': 3, 'warn': 2, 'error': 1, 'silent': 0 };\r\n// const CURRENT_LOG_LEVEL = LOG_LEVELS[logLevel] ?? LOG_LEVELS.info;\r\n\r\n// const logger: Logger = {\r\n// debug: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.debug) {\r\n// if (typeof externalLogger.debug === 'function') {\r\n// externalLogger.debug(`[DEBUG] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[DEBUG] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// info: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.info) {\r\n// // التحقق من .info (ممارسة جيدة)\r\n// if (typeof externalLogger.info === 'function') {\r\n// externalLogger.info(message, ...args);\r\n// } else {\r\n// externalLogger.log(message, ...args);\r\n// }\r\n// }\r\n// },\r\n// warn: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.warn) {\r\n// // التحقق من .warn (ممارسة جيدة)\r\n// if (typeof externalLogger.warn === 'function') {\r\n// externalLogger.warn(`[WARN] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[WARN] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// error: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.error) {\r\n// // التحقق من .error (ممارسة جيدة)\r\n// if (typeof externalLogger.error === 'function') {\r\n// externalLogger.error(`[ERROR] ${message}`, ...args);\r\n// } else {\r\n// externalLogger.log(`[ERROR] ${message}`, ...args);\r\n// }\r\n// }\r\n// },\r\n// log: (message, ...args) => {\r\n// if (CURRENT_LOG_LEVEL >= LOG_LEVELS.debug) {\r\n// // .log دائمًا موجود في تعريف الواجهة، لذا لا حاجة للتحقق\r\n// externalLogger.log(`[DEBUG] ${message}`, ...args);\r\n// }\r\n// },\r\n// };\r\n \r\n// const axiosInstance = axios.create({\r\n// baseURL,\r\n// timeout,\r\n// headers: { 'Content-Type': 'application/json', ...headers },\r\n// withCredentials,\r\n// });\r\n\r\n// let isRefreshing = false;\r\n// let failedQueue: { resolve: (value: any) => void; reject: (reason?: any) => void }[] = [];\r\n\r\n// const processQueue = (error: any, token: string | null = null) => {\r\n// failedQueue.forEach(prom => (error ? prom.reject(error) : prom.resolve(token)));\r\n// failedQueue = [];\r\n// };\r\n\r\n// // --- Request Interceptor ---\r\n// axiosInstance.interceptors.request.use(async (req: InternalAxiosRequestConfig & RequestConfig) => {\r\n// req.headers['X-Request-ID'] = uuidv4();\r\n\r\n// // ✅ NEW: Construct and log the full URL.\r\n// const fullUrl = axios.getUri({ ...req, baseURL: req.baseURL ?? baseURL });\r\n// logger.info(`[Request] > ${req.method?.toUpperCase()} ${fullUrl}`, { id: req.headers['X-Request-ID'] });\r\n// logger.debug('Request Details:', { headers: req.headers, data: req.data });\r\n\r\n// const context: MiddlewareContext = { req, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n\r\n// const isRequestPublic = req.isPublic ?? defaultIsPublic;\r\n\r\n// if (isRequestPublic) {\r\n// logger.debug('[API Core] Request is public, skipping token attachment.');\r\n// return req; \r\n// }\r\n \r\n// if (tokenManager.isHttpOnly()) return req;\r\n\r\n// let tokens = await tokenManager.getTokens();\r\n// const now = Date.now();\r\n// const tokenBuffer = 60 * 1000;\r\n\r\n// if (tokens.accessToken && tokens.expiresAt && (tokens.expiresAt - now < tokenBuffer) && !isRefreshing) {\r\n// if (config.refreshTokenConfig) {\r\n// logger.info('[API Core] Proactive token refresh initiated.');\r\n// isRefreshing = true;\r\n// try {\r\n// const newTokens = await refreshToken(config, tokenManager, logger);\r\n// if (newTokens) tokens = newTokens;\r\n// } finally {\r\n// isRefreshing = false;\r\n// }\r\n// }\r\n// }\r\n\r\n// if (tokens.accessToken) {\r\n// req.headers.Authorization = `${tokens.tokenType || 'Bearer'} ${tokens.accessToken}`;\r\n// }\r\n\r\n// return req;\r\n// });\r\n\r\n// // --- Response Interceptor ---\r\n// axiosInstance.interceptors.response.use(\r\n// async (res: AxiosResponse) => {\r\n// const fullUrl = axios.getUri(res.config);\r\n// logger.info(`[Response] < ${res.config.method?.toUpperCase()} ${fullUrl}`, { id: res.config.headers['X-Request-ID'], status: res.status });\r\n// logger.debug('Response Data:', res.data);\r\n\r\n// const context: MiddlewareContext = { req: res.config, res, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n// return res;\r\n// },\r\n// async (error: AxiosError<ApiError>) => {\r\n// const originalRequest = error.config as InternalAxiosRequestConfig & RequestConfig & { _retry?: boolean };\r\n// const fullUrl = axios.getUri(originalRequest);\r\n \r\n// logger.error(`[Response Error] < ${originalRequest.method?.toUpperCase()} ${fullUrl}`, {\r\n// id: originalRequest.headers?.['X-Request-ID'],\r\n// status: error.response?.status,\r\n// message: error.message,\r\n// });\r\n\r\n// logger.debug('Full Error Object:', error.toJSON ? error.toJSON() : error);\r\n \r\n// const context: MiddlewareContext = { req: originalRequest, error, logger, custom: {} };\r\n// await runMiddleware(context, middleware);\r\n\r\n// // ... (rest of the token refresh logic is unchanged) ...\r\n// const isHttpOnlyMode = tokenManager.isHttpOnly();\r\n// const canAttemptRefresh = error.response?.status === 401 && !originalRequest._retry && config.refreshTokenConfig;\r\n\r\n// if (isHttpOnlyMode && canAttemptRefresh) {\r\n// logger.error(\"[API Core] Fatal: Received 401 in httpOnly mode. Token refresh must be handled by the server-side proxy. The request will not be retried from the client.\");\r\n// return Promise.reject(error);\r\n// }\r\n \r\n// if (!isHttpOnlyMode && canAttemptRefresh) {\r\n// if (isRefreshing) {\r\n// return new Promise((resolve, reject) => {\r\n// failedQueue.push({ resolve, reject });\r\n// }).then(token => {\r\n// originalRequest.headers['Authorization'] = `Bearer ${token}`;\r\n// return axiosInstance(originalRequest);\r\n// });\r\n// }\r\n \r\n// originalRequest._retry = true;\r\n// isRefreshing = true;\r\n// try {\r\n// const newTokens = await refreshToken(config, tokenManager, logger);\r\n// if (!newTokens || !newTokens.accessToken) {\r\n// throw new Error('Token refresh failed to produce a new access token.');\r\n// }\r\n// processQueue(null, newTokens.accessToken);\r\n// originalRequest.headers['Authorization'] = `${newTokens.tokenType || 'Bearer'} ${newTokens.accessToken}`;\r\n// return axiosInstance(originalRequest);\r\n// } catch (refreshError) {\r\n// processQueue(refreshError, null);\r\n// return Promise.reject(refreshError);\r\n// } finally {\r\n// isRefreshing = false;\r\n// }\r\n// }\r\n\r\n// return Promise.reject(error);\r\n// }\r\n// );\r\n\r\n// return axiosInstance;\r\n// }","import axios, { AxiosError, AxiosResponse } from 'axios';\r\nimport { ApiError, StandardResponse, ValidationError } from '../types';\r\n// [مهم] استيراد دوال الحماية الجديدة\r\nimport { isServerError, isNetworkError, isAxiosResponse } from './utils';\r\n\r\n/**\r\n * [النسخة النهائية والمضمونة]\r\n * تستخدم دوال حماية النوع المخصصة للقضاء على أخطاء 'never' بشكل نهائي.\r\n */\r\nexport const processResponse = <T>(\r\n responseOrError: AxiosResponse<any> | AxiosError,\r\n): StandardResponse<T> => {\r\n \r\n // ===================================================================================\r\n // #region المسار 1: معالجة النجاح\r\n // ===================================================================================\r\n if (isAxiosResponse(responseOrError)) {\r\n const response = responseOrError;\r\n const rawData = response.data;\r\n const isStandardApiResponse = rawData && typeof rawData.success === 'boolean' && rawData.data !== undefined;\r\n \r\n return {\r\n data: isStandardApiResponse ? rawData.data : rawData,\r\n links: isStandardApiResponse ? rawData.links : undefined,\r\n meta: isStandardApiResponse ? rawData.meta : undefined,\r\n rawResponse: rawData,\r\n loading: false, success: true, error: null,\r\n message: isStandardApiResponse ? rawData.message : 'Request successful.',\r\n validationErrors: [],\r\n };\r\n }\r\n\r\n // ===================================================================================\r\n // #region المسار 2: معالجة الأخطاء\r\n // ===================================================================================\r\n\r\n // الحالة 2.1: خطأ من الخادم (4xx/5xx)\r\n // داخل هذا البلوك، TypeScript متأكد 100% أن `responseOrError.response` موجود.\r\n if (isServerError(responseOrError)) {\r\n const error = responseOrError; // الآن `error` من النوع الصحيح\r\n type ApiErrorResponse = { message?: string; code?: string; errors?: ValidationError[] };\r\n const responseData = error.response.data as ApiErrorResponse | undefined;\r\n const status = error.response.status;\r\n let defaultMessage = 'An unexpected server error occurred.';\r\n\r\n switch (status) {\r\n case 400: defaultMessage = 'Bad Request'; break;\r\n case 401: defaultMessage = 'Unauthorized'; break;\r\n case 403: defaultMessage = 'Forbidden'; break;\r\n case 404: defaultMessage = 'Not Found'; break;\r\n case 422: defaultMessage = 'Validation Failed'; break;\r\n case 500: defaultMessage = 'Internal Server Error'; break;\r\n case 503: defaultMessage = 'Service Unavailable'; break;\r\n default: defaultMessage = `Server Error (${status})`;\r\n }\r\n\r\n const finalApiError: ApiError = {\r\n message: responseData?.message || defaultMessage,\r\n status: status,\r\n code: responseData?.code || error.code,\r\n errors: responseData?.errors || [],\r\n };\r\n \r\n return {\r\n data: null, rawResponse: responseData,\r\n error: finalApiError,\r\n validationErrors: finalApiError.errors,\r\n success: false, loading: false, message: finalApiError.message,\r\n };\r\n }\r\n\r\n // الحالة 2.2: خطأ في الشبكة\r\n // داخل هذا البلوك، TypeScript متأكد 100% أن `responseOrError.request` موجود.\r\n if (isNetworkError(responseOrError)) {\r\n const error = responseOrError;\r\n return {\r\n data: null, rawResponse: error.request,\r\n error: { message: 'Network Error: Unable to connect.', status: 0, code: error.code },\r\n success: false, loading: false, message: 'Network Error.',\r\n };\r\n }\r\n \r\n // الحالة 2.3: تم إلغاء الطلب\r\n if (axios.isCancel(responseOrError)) {\r\n return {\r\n data: null, rawResponse: null,\r\n error: { message: 'Request Canceled.', status: 499 },\r\n success: false, loading: false, message: 'Request Canceled.',\r\n };\r\n }\r\n\r\n // ===================================================================================\r\n // #region المسار الأخير: الحالة الاحتياطية\r\n // ===================================================================================\r\n \r\n // لأي خطأ آخر لم تتم معالجته (مثل أخطاء الإعداد)\r\n return {\r\n data: null, rawResponse: responseOrError,\r\n error: { message: 'An unknown error occurred.', status: -1 },\r\n success: false, loading: false, message: 'An unknown error occurred.',\r\n };\r\n};\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // file: src/core/processor.ts (مُحدَّث)\r\n\r\n// import { AxiosResponse } from 'axios';\r\n// import { ApiError, StandardResponse } from '../types';\r\n// import { isApiError, isAxiosResponse } from './utils';\r\n\r\n// export const processResponse = <T>(\r\n// responseOrError: AxiosResponse<any> | ApiError,\r\n// ): StandardResponse<T> => {\r\n// console.log(\"responseOrError [lib]\" , responseOrError)\r\n// console.log(\"responseOrError.data [lib]\" , responseOrError?.status)\r\n\r\n// if (isApiError(responseOrError)) {\r\n// return {\r\n// data: null,\r\n// rawResponse: undefined,\r\n// error: responseOrError,\r\n// validationErrors: responseOrError.errors || [],\r\n// success: false,\r\n// loading: false,\r\n// message: responseOrError.message,\r\n// };\r\n// }\r\n \r\n// if (isAxiosResponse(responseOrError)) {\r\n// const rawData = responseOrError.data;\r\n// const isWrappedResponse = \r\n// rawData && \r\n// typeof rawData.success === 'boolean' && \r\n// rawData.data !== undefined;\r\n \r\n// const finalData: T = isWrappedResponse ? rawData.data : rawData;\r\n// const message = isWrappedResponse ? rawData.message : 'Request successful.';\r\n\r\n// return {\r\n// data: finalData,\r\n// rawResponse: rawData,\r\n// loading: false,\r\n// success: true,\r\n// error: null,\r\n// message: message,\r\n// validationErrors: [],\r\n// };\r\n// }\r\n\r\n// return {\r\n// data: null,\r\n// rawResponse: undefined,\r\n// error: { message: 'An unknown error occurred during response processing.', status: 500 },\r\n// success: false,\r\n// loading: false,\r\n// message: 'An unknown error occurred.',\r\n// validationErrors: []\r\n// };\r\n// };","import axios, { AxiosError, AxiosResponse, CancelTokenSource } from 'axios';\r\nimport { ApiError, QueryOptions, StandardResponse } from '../types';\r\n\r\n// ===================================================================================\r\n// #region Type Guards (التحقق من الأنواع)\r\n// ===================================================================================\r\n\r\n/**\r\n * يتحقق مما إذا كان الكائن هو خطأ مخصص من نوع ApiError.\r\n */\r\nexport function isApiError(obj: any): obj is ApiError {\r\n return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n}\r\n\r\n\r\n// الدوال القديمة يمكن الإبقاء عليها للتوافق أو استبدالها بالكامل\r\nexport function isAxiosError(obj: any): obj is AxiosError {\r\n return obj && obj.isAxiosError === true;\r\n}\r\n\r\nexport function isAxiosResponse(obj: any): obj is AxiosResponse {\r\n return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n}\r\n\r\n\r\n\r\n\r\n// ===================================================================================\r\n// #region URL & Query Handling (معالجة روابط URL والاستعلامات)\r\n// ===================================================================================\r\n\r\n/**\r\n * يبني رابطًا ديناميكيًا عن طريق استبدال المتغيرات المؤقتة مثل {key} بالقيم من كائن.\r\n * @param template - قالب الرابط (مثال: '/users/{userId}/posts').\r\n * @param params - كائن يحتوي على قيم المتغيرات (مثال: { userId: 123 }).\r\n * @returns الرابط النهائي بعد المعالجة.\r\n */\r\nexport function buildDynamicUrl(template: string, params?: Record<string, any>): string {\r\n if (!params) {\r\n return template;\r\n }\r\n return template.replace(/\\{(\\w+)\\}/g, (placeholder, key) => {\r\n return params.hasOwnProperty(key) ? String(params[key]) : placeholder;\r\n });\r\n}\r\n\r\n/**\r\n * [نسخة مطورة] يبني سلسلة استعلام (query string) من كائن الخيارات.\r\n * يدعم الآن الفلاتر المتداخلة (filter[key]=value) والترتيب المتعدد.\r\n * @param options - كائن خيارات الاستعلام (فلترة, ترتيب, ...).\r\n * @returns سلسلة استعلام جاهزة للإضافة للرابط.\r\n */\r\nexport function buildPaginateQuery(options: QueryOptions): string {\r\n const params = new URLSearchParams();\r\n\r\n for (const key in options) {\r\n const value = options[key];\r\n\r\n if (value === null || value === undefined) {\r\n continue; // تجاهل القيم الفارغة\r\n }\r\n\r\n if (key === 'filter' && typeof value === 'object' && !Array.isArray(value)) {\r\n // التعامل مع الفلاتر المتداخلة\r\n for (const filterKey in value) {\r\n const filterValue = value[filterKey];\r\n if (filterValue !== null && filterValue !== undefined) {\r\n params.append(`filter[${filterKey}]`, String(filterValue));\r\n }\r\n }\r\n } else if (key === 'sortBy' && Array.isArray(value)) {\r\n // التعامل مع الترتيب المتعدد\r\n value.forEach(sortItem => {\r\n if (sortItem && sortItem.key && sortItem.direction) {\r\n params.append('sortBy[]', `${sortItem.key}:${sortItem.direction}`);\r\n }\r\n });\r\n } else {\r\n // التعامل مع باقي المعاملات\r\n params.append(key, String(value));\r\n }\r\n }\r\n\r\n return params.toString();\r\n}\r\n\r\n// ===================================================================================\r\n// #region Object & Data Helpers (أدوات مساعدة للكائنات والبيانات)\r\n// ===================================================================================\r\n\r\n/**\r\n * يتحقق مما إذا كانت الاستجابة تحتوي على بيانات قابلة للعرض.\r\n * @param response - كائن StandardResponse.\r\n * @returns `true` إذا كانت البيانات موجودة وليست مصفوفة فارغة.\r\n */\r\nexport function hasData<T>(response: StandardResponse<T> | null | undefined): boolean {\r\n if (!response || !response.success || response.data === null || response.data === undefined) {\r\n return false;\r\n }\r\n if (Array.isArray(response.data) && response.data.length === 0) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * ينشئ نسخة جديدة من كائن مع إزالة كل الخصائص التي قيمتها `null` أو `undefined`.\r\n * @param obj - الكائن المراد تنظيفه.\r\n * @returns نسخة نظيفة من الكائن.\r\n */\r\nexport function cleanObject<T extends Record<string, any>>(obj: T): Partial<T> {\r\n const newObj: Partial<T> = {};\r\n for (const key in obj) {\r\n if (obj[key] !== null && obj[key] !== undefined) {\r\n newObj[key] = obj[key];\r\n }\r\n }\r\n return newObj;\r\n}\r\n\r\n// ===================================================================================\r\n// #region Async Helpers (أدوات مساعدة غير متزامنة)\r\n// ===================================================================================\r\n\r\n/**\r\n * ينشئ دالة تقوم بتأخير تنفيذ الدالة الأصلية حتى انقضاء فترة زمنية معينة\r\n * بعد آخر مرة تم استدعاؤها. مفيدة جدًا لتقليل الطلبات في حقول البحث.\r\n * @param func - الدالة التي سيتم تأخير تنفيذها.\r\n * @param delay - مدة التأخير بالمللي ثانية.\r\n * @returns دالة جديدة قابلة للاستدعاء.\r\n */\r\nexport function debounce<T extends (...args: any[]) => any>(func: T, delay: number): (...args: Parameters<T>) => void {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n return function(...args: Parameters<T>) {\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n timeoutId = setTimeout(() => {\r\n func(...args);\r\n }, delay);\r\n };\r\n}\r\n\r\n/**\r\n * دالة بسيطة لإيقاف التنفيذ لمدة زمنية محددة.\r\n * @param ms - مدة التأخير بالمللي ثانية.\r\n * @returns Promise يكتمل بعد انقضاء المدة.\r\n */\r\nexport function delay(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n// ===================================================================================\r\n// #region Request Cancellation (إدارة إلغاء الطلبات)\r\n// ===================================================================================\r\n\r\nconst cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n/**\r\n * ينشئ cancel token جديد لطلب معين، ويلغي أي طلب قديم بنفس المفتاح.\r\n * يمنع حالات الـ race conditions في المكونات التي ترسل طلبات متتالية.\r\n * @param key - مفتاح فريد لتعريف الطلب (مثال: 'user-search').\r\n * @returns كائن CancelTokenSource من Axios.\r\n */\r\nexport const createCancelToken = (key: string): CancelTokenSource => {\r\n const existingToken = cancelTokens.get(key);\r\n if (existingToken) {\r\n existingToken.cancel('Operation canceled due to a new request.');\r\n }\r\n const source = axios.CancelToken.source();\r\n cancelTokens.set(key, source);\r\n return source;\r\n};\r\n\r\n\r\n\r\n\r\n\r\n// ===================================================================================\r\n// #region Advanced Type Guards (أدوات تحقق متقدمة من الأنواع)\r\n// ===================================================================================\r\n\r\n/**\r\n * [جديد وحاسم] يتحقق مما إذا كان الخطأ هو خطأ من الخادم (يحتوي على `response`).\r\n * الأهم من ذلك، أنه يضيق نوع الخطأ ليضمن وجود `response`.\r\n */\r\nexport function isServerError(error: any): error is AxiosError & { response: AxiosResponse } {\r\n return axios.isAxiosError(error) && error.response !== undefined;\r\n}\r\n\r\n/**\r\n * [جديد وحاسم] يتحقق مما إذا كان الخطأ هو خطأ في الشبكة (لا يحتوي على `response` ولكن يحتوي على `request`).\r\n * يضيق نوع الخطأ ليضمن عدم وجود `response`.\r\n */\r\nexport function isNetworkError(error: any): error is AxiosError & { request: any } {\r\n return axios.isAxiosError(error) && error.response === undefined && error.request !== undefined;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// /**\r\n// * @file src/core/utils.ts\r\n// * @description\r\n// * يحتوي هذا الملف على مجموعة من الدوال المساعدة المستخدمة في جميع أنحاء المكتبة،\r\n// * مثل بناء سلاسل الاستعلام (query strings) وإدارة إلغاء الطلبات،\r\n// * بالإضافة إلى دوال التحقق من الأنواع (type guards).\r\n// */\r\n\r\n// import axios, { CancelTokenSource } from 'axios';\r\n// import { ApiError, QueryOptions } from '../types';\r\n// import { AxiosResponse } from 'axios';\r\n\r\n// // ===================================\r\n// // إدارة إلغاء الطلبات\r\n// // ===================================\r\n// const cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n// export const createCancelToken = (key: string): CancelTokenSource => {\r\n// const existingToken = cancelTokens.get(key);\r\n// if (existingToken) {\r\n// existingToken.cancel('Operation canceled due to a new request.');\r\n// cancelTokens.delete(key);\r\n// }\r\n// const source = axios.CancelToken.source();\r\n// cancelTokens.set(key, source);\r\n// return source;\r\n// };\r\n\r\n\r\n\r\n// interface BuildQueryOptions {\r\n// encode?: boolean;\r\n// }\r\n\r\n// /**\r\n// * يبني سلسلة استعلام (query string) من كائن الخيارات.\r\n// * @param options - كائن خيارات الاستعلام (فلترة, ترتيب, ...).\r\n// * @param config - إعدادات إضافية مثل التحكم في التشفير.\r\n// * @returns سلسلة استعلام جاهزة للإضافة للرابط.\r\n// */\r\n// export function buildPaginateQuery(options: QueryOptions, config: BuildQueryOptions = { encode: true }): string {\r\n// // استخدام URLSearchParams يقوم بالتشفير تلقائيًا وهو الأسلوب القياسي والآمن\r\n// if (config.encode) {\r\n// const params = new URLSearchParams();\r\n// Object.entries(options).forEach(([key, value]) => {\r\n// if (value !== undefined && value !== null) {\r\n// // يمكنك إضافة منطق أكثر تعقيدًا هنا للتعامل مع المصفوفات أو الكائنات\r\n// params.append(key, String(value));\r\n// }\r\n// });\r\n// return params.toString();\r\n// }\r\n\r\n// // منطق بديل إذا كان encode = false (لـ APIs التي تتطلب تنسيقًا خاصًا)\r\n// const parts = Object.entries(options)\r\n// .filter(([, value]) => value !== undefined && value !== null)\r\n// .map(([key, value]) => `${key}=${String(value)}`);\r\n \r\n// return parts.join('&');\r\n// }\r\n\r\n// // ===================================\r\n// // التحقق من الأنواع (Type Guards)\r\n// // ===================================\r\n// export function isApiError(obj: any): obj is ApiError {\r\n// console.log(\"obj in IsApiError\" , obj)\r\n// return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n// }\r\n\r\n// export function isAxiosResponse(obj: any): obj is AxiosResponse {\r\n// return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // import axios, { CancelTokenSource } from 'axios';\r\n// // import { PaginateQueryOptions, ApiError } from '../types';\r\n\r\n// // // === Cancel Token Management ===\r\n// // const cancelTokens = new Map<string, CancelTokenSource>();\r\n\r\n// // export const createCancelToken = (key: string): CancelTokenSource => {\r\n// // const existingToken = cancelTokens.get(key);\r\n// // if (existingToken) {\r\n// // existingToken.cancel('Operation canceled due to new request.');\r\n// // cancelTokens.delete(key);\r\n// // }\r\n// // const source = axios.CancelToken.source();\r\n// // cancelTokens.set(key, source);\r\n// // return source;\r\n// // };\r\n\r\n// // export const cancelRequest = (key: string): void => {\r\n// // const source = cancelTokens.get(key);\r\n// // if (source) {\r\n// // source.cancel('Operation canceled by user.');\r\n// // cancelTokens.delete(key);\r\n// // }\r\n// // };\r\n\r\n// // export const cancelAllRequests = (): void => {\r\n// // cancelTokens.forEach(source => source.cancel('All operations canceled.'));\r\n// // cancelTokens.clear();\r\n// // };\r\n\r\n// // // === Query Builder ===\r\n// // export function buildPaginateQuery(query: PaginateQueryOptions): string {\r\n// // const params = new URLSearchParams();\r\n// // if (!query) return '';\r\n\r\n// // if (query.page) params.append('page', query.page.toString());\r\n// // if (query.limit) params.append('limit', query.limit.toString());\r\n// // if (query.search) params.append('search', query.search);\r\n// // if (query.sortBy) {\r\n// // query.sortBy.forEach(sort => params.append('sortBy', `${sort.key}:${sort.direction}`));\r\n// // }\r\n// // if (query.filter) {\r\n// // Object.entries(query.filter).forEach(([field, value]) => {\r\n// // params.append(`filter.${field}`, value);\r\n// // });\r\n// // }\r\n// // const queryString = params.toString();\r\n// // return queryString ? `?${queryString}` : '';\r\n// // }\r\n\r\n// // // === Type Guards ===\r\n// // export function isApiError(obj: any): obj is ApiError {\r\n// // return obj && typeof obj.status === 'number' && typeof obj.message === 'string' && obj.config === undefined;\r\n// // }\r\n\r\n// // export function isAxiosResponse(obj: any): obj is any {\r\n// // return obj && obj.data !== undefined && obj.status !== undefined && obj.config !== undefined;\r\n// // }","/**\r\n * Replaces path placeholders like {key} with values from a params object.\r\n * @param template - The URL template (e.g., '/users/{userId}/posts').\r\n * @param params - An object with keys matching the placeholders (e.g., { userId: 123 }).\r\n * @returns The final, resolved URL string.\r\n */\r\nexport function buildDynamicUrl(template: string, params?: Record<string, any>): string {\r\n if (!params) {\r\n return template;\r\n }\r\n return template.replace(/\\{(\\w+)\\}/g, (placeholder, key) => {\r\n return params.hasOwnProperty(key) ? String(params[key]) : placeholder;\r\n });\r\n}","// file: src/services/crud.ts\r\n\r\nimport { AxiosInstance, AxiosRequestConfig } from 'axios';\r\nimport { ActionOptions, RequestConfig, StandardResponse } from '../types';\r\nimport { processResponse } from '../core/processor';\r\nimport { buildDynamicUrl } from '@/core/buildDynamicUrl';\r\nimport { ActionConfigModule } from '@/types/apiModule.types';\r\n\r\n/**\r\n * A factory function to create a reusable set of API services for a specific endpoint.\r\n * It provides full CRUD operations plus advanced features like bulk deletion and file uploads,\r\n * with intelligent, dynamic URL building.\r\n */\r\nexport function createApiServices<T>(axiosInstance: AxiosInstance, baseEndpoint: string) {\r\n \r\n /**\r\n * Intelligently resolves the final URL for a request.\r\n * Priority:\r\n * 1. A full endpoint override from `config.endpoint`.\r\n * 2. The base endpoint appended with an ID, if provided.\r\n * 3. The base endpoint alone.\r\n * It also replaces dynamic segments like {id} or {tenantId} in the URL.\r\n */\r\n const resolveUrl = (config: ActionOptions = {}, id?: string | number): string => {\r\n const endpointTemplate = config.endpoint || (id != null ? `${baseEndpoint}/{id}` : baseEndpoint);\r\n \r\n // Create a params object for URL building. We include common ID variations.\r\n const params = id != null ? { id, tenant: id, tenantId: id, recordId: id } : {};\r\n \r\n return buildDynamicUrl(endpointTemplate, params);\r\n };\r\n\r\n /**\r\n * Fetches data. Can fetch a list or a single record based on whether an ID is provided.\r\n * This is the primary flexible read operation.\r\n */\r\n const get = async (id?: string | number, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.get(url, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Fetches a list of records using a query string.\r\n * This is specifically for fetching paginated/filtered lists.\r\n */\r\n const getWithQuery = async (query: string, config?: RequestConfig): Promise<StandardResponse<T>> => {\r\n const url = `${baseEndpoint}?${query}`;\r\n try {\r\n const response = await axiosInstance.get(url, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new resource.\r\n */\r\n const post = async (data: Partial<T>, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config);\r\n try {\r\n const response = await axiosInstance.post(url, data, config);\r\n console.log(\"[lib] response POST: \" , response)\r\n console.log(\"[lib] response processResponse POST: \" , processResponse<T>(response))\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n console.log(\"[lib] response error POST: \" , processResponse<T>(error as any))\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Replaces an entire resource.\r\n */\r\n const put = async (id: string | number, data: T, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.put(url, data, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Partially updates a resource.\r\n */\r\n const patch = async (id: string | number, data: Partial<T>, config?: ActionOptions): Promise<StandardResponse<T>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.patch(url, data, config);\r\n return processResponse<T>(response);\r\n } catch (error) {\r\n return processResponse<T>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Deletes a resource.\r\n */\r\n const remove = async (id: string | number, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config, id);\r\n try {\r\n const response = await axiosInstance.delete(url, config);\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n \r\n /**\r\n * Deletes multiple resources in a single request.\r\n */\r\n const bulkDelete = async (ids: Array<string | number>, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config); // Bulk actions typically don't use an ID in the path.\r\n try {\r\n const response = await axiosInstance.delete(url, { data: { ids }, ...config });\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n\r\n /**\r\n * Uploads a file, with optional additional data.\r\n */\r\n const upload = async (file: File, additionalData?: Record<string, any>, config?: ActionOptions): Promise<StandardResponse<any>> => {\r\n const url = resolveUrl(config);\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n if (additionalData) {\r\n Object.keys(additionalData).forEach(key => formData.append(key, String(additionalData[key])));\r\n }\r\n try {\r\n const response = await axiosInstance.post(url, formData, {\r\n ...config, headers: { ...config?.headers, 'Content-Type': 'multipart/form-data' },\r\n });\r\n return processResponse<any>(response);\r\n } catch (error) {\r\n return processResponse<any>(error as any);\r\n }\r\n };\r\n\r\n return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// نفترض أن هذه الدوال موجودة في ملف مساعد\r\n// import { buildDynamicUrl } from '@/core/buildDynamicUrl';\r\n// import { processResponse } from '../core/processor';\r\n\r\n/**\r\n * [نسخة محدثة ومحسّنة]\r\n * دالة عامة وصريحة لتنفيذ أي طلب API.\r\n * تستقبل وسائط منظمة بدلاً من محاولة تخمينها.\r\n * \r\n * @param axiosInstance - نسخة Axios.\r\n * @param baseEndpoint - نقطة النهاية الأساسية للموديول.\r\n * @param actionConfig - إعدادات الإجراء المحدد.\r\n * @param params - كائن يحتوي على جميع البارامترات اللازمة للطلب.\r\n * @returns Promise<StandardResponse<any>>\r\n */\r\nexport async function callDynamicApi(\r\n axiosInstance: AxiosInstance,\r\n baseEndpoint: string,\r\n actionConfig: ActionConfigModule<any, any>,\r\n params: {\r\n pathParams?: Record<string, string | number>;\r\n body?: any;\r\n config?: AxiosRequestConfig;\r\n }\r\n): Promise<StandardResponse<any>> {\r\n \r\n // 1. استخراج البارامترات بشكل صريح وواضح\r\n const { pathParams, body, config } = params;\r\n\r\n // 2. بناء الـ URL الديناميكي\r\n const urlTemplate = `${baseEndpoint}${actionConfig.path}`;\r\n // افترض وجود هذه الدوال أو قم بإنشائها\r\n const finalUrl = buildDynamicUrl(urlTemplate, pathParams || {}); \r\n\r\n try {\r\n const { method } = actionConfig;\r\n let response;\r\n\r\n // 3. تنفيذ الطلب بناءً على نوع الـ Method\r\n // أصبح المنطق الآن أكثر نظافة ووضوحًا\r\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\r\n response = await axiosInstance[method.toLowerCase() as 'post'](finalUrl, body, config);\r\n } else if (method === 'DELETE') {\r\n // DELETE قد يحتوي على body في بعض الحالات (مثل bulk)\r\n response = await axiosInstance.delete(finalUrl, { data: body, ...config });\r\n } else { // GET\r\n // في GET، الجسم (body) يمثل معاملات الاستعلام (query params)\r\n response = await axiosInstance.get(finalUrl, { params: body, ...config });\r\n }\r\n \r\n return processResponse(response);\r\n } catch (error) {\r\n // معالجة الأخطاء تبقى كما هي\r\n return processResponse(error as any);\r\n }\r\n}\r\n\r\n\r\n\r\n\r\n// import { AxiosInstance } from 'axios';\r\n// import { ActionOptions, RequestConfig, StandardResponse } from '../types';\r\n// import { processResponse } from '../core/processor';\r\n\r\n// type CrudRequestConfig = RequestConfig & ActionOptions;\r\n\r\n// /**\r\n// * دالة مصنع (Factory Function) لإنشاء مجموعة خدمات API قابلة لإعادة الاستخدام لنقطة نهاية (endpoint) محددة.\r\n// * توفر عمليات CRUD كاملة بالإضافة إلى ميزات متقدمة مثل الحذف الجماعي ورفع الملفات.\r\n// */\r\n// export function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string) {\r\n \r\n// // --- Read Operations ---\r\n// const get = async (id?: string, config?: RequestConfig): Promise<StandardResponse<T | T[]>> => {\r\n// const url = id ? `${endpoint}/${id}` : endpoint;\r\n// try {\r\n// const response = await axiosInstance.get(url, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// const getWithQuery = async (query: string, config?: RequestConfig): Promise<StandardResponse<T[]>> => {\r\n// try {\r\n// const response = await axiosInstance.get(`${endpoint}${query}`, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// // --- Write Operations ---\r\n// const post = async (data: Partial<T>, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || endpoint;\r\n// try {\r\n// const response = await axiosInstance.post(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة PUT\r\n// * تستخدم لاستبدال مورد بالكامل.\r\n// * لاحظ أن `data` من النوع `T` وليس `Partial<T>` لأن PUT يتوقع المورد كاملاً.\r\n// */\r\n// const put = async (id: string, data: T, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.put(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// const patch = async (id: string, data: Partial<T>, config?: CrudRequestConfig): Promise<StandardResponse<T>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.patch(finalUrl, data, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// const remove = async (id: string, config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}/${id}`;\r\n// try {\r\n// const response = await axiosInstance.delete(finalUrl, config);\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n \r\n// // --- Advanced Operations ---\r\n\r\n// /**\r\n// * ✅ NEW: إضافة دالة للحذف الجماعي (Bulk Delete)\r\n// * مفيدة جدًا لحذف عدة عناصر في طلب واحد لتحسين الأداء.\r\n// * ترسل مصفوفة من الـ IDs في جسم الطلب (request body).\r\n// */\r\n// const bulkDelete = async (ids: string[], config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}`; // يمكن تخصيص المسار\r\n// try {\r\n// // Axios's delete method can send a body via the config object\r\n// const response = await axiosInstance.delete(finalUrl, { data: { ids }, ...config });\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// /**\r\n// * ✅ NEW: إضافة دالة لرفع الملفات (File Upload)\r\n// * تتعامل مع `FormData` لرفع الملفات، وهي ميزة أساسية في معظم التطبيقات.\r\n// * يمكن إرسال بيانات إضافية مع الملف.\r\n// */\r\n// const upload = async (file: File, additionalData?: Record<string, any>, config?: CrudRequestConfig): Promise<StandardResponse<any>> => {\r\n// const finalUrl = config?.endpoint || `${endpoint}`;\r\n \r\n// const formData = new FormData();\r\n// formData.append('file', file); // 'file' هو اسم الحقل الشائع، يمكن تغييره\r\n \r\n// if (additionalData) {\r\n// Object.keys(additionalData).forEach(key => {\r\n// formData.append(key, additionalData[key]);\r\n// });\r\n// }\r\n\r\n// try {\r\n// const response = await axiosInstance.post(finalUrl, formData, {\r\n// ...config,\r\n// headers: {\r\n// ...config?.headers,\r\n// 'Content-Type': 'multipart/form-data',\r\n// },\r\n// });\r\n// return processResponse(response);\r\n// } catch (error) {\r\n// return processResponse(error as any);\r\n// }\r\n// };\r\n\r\n// return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","\r\nimport { AxiosInstance, Method } from 'axios';\r\nimport { processResponse } from '../core/processor';\r\nimport { RequestConfig, StandardResponse } from '@/types';\r\n\r\n/**\r\n * Defines a single custom API action.\r\n * @template TRequest - The type of the data sent in the request body/params.\r\n * @template TResponse - The type of the data expected in the successful API response.\r\n */\r\ntype ApiAction<TRequest, TResponse> = (\r\n payload: TRequest,\r\n config?: RequestConfig\r\n) => Promise<StandardResponse<TResponse>>;\r\n\r\n// type ApiAction<TRequest, TResponse> = TRequest extends void\r\n// ? (payload?: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>\r\n// : (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;\r\n \r\nfunction createAction<TRequest, TResponse>(\r\n axiosInstance: AxiosInstance,\r\n method: Method,\r\n endpoint: string,\r\n // log?:boolean,\r\n): ApiAction<TRequest, TResponse> {\r\n return async (payload: TRequest, config?: RequestConfig) => {\r\n// return async (payload: TRequest = {} as TRequest, config?: RequestConfig) => {\r\n // log && console.log(`*[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} **** [${payload}]** `)\r\n try {\r\n // log && console.log(`**[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]:${endpoint} **** [action try req]** `)\r\n const response = await axiosInstance.request<any>({\r\n url: endpoint,\r\n method,\r\n ...(method.toUpperCase() === 'GET' ? { params: payload } : { data: payload }),\r\n ...config,\r\n });\r\n // log && console.log(`***[ ${method.toUpperCase()} ACTION API TO ENDPOINT ]: ${endpoint} ***\\n ${response}*** `)\r\n return processResponse<TResponse>(response);\r\n } catch (error: any) {\r\n return processResponse(error);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * A factory function to create a collection of typed, custom API actions.\r\n * \r\n * @template TActions - An object type where keys are action names and values are objects\r\n * defining the endpoint, method, and types for that action.\r\n * @param axiosInstance - The configured Axios instance from `createApiClient`.\r\n * @param actionsConfig - An object defining the configuration for each custom action.\r\n * @returns A fully-typed object of executable API action functions.\r\n * \r\n * @example\r\n * const authActions = createApiActions(apiClient, {\r\n * login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },\r\n * getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }\r\n * });\r\n * \r\n * // Usage:\r\n * const result = await authActions.login({ email: '..', password: '..' });\r\n */\r\nexport function createApiActions<\r\n TActions extends Record<string, { \r\n method: Method; \r\n endpoint: string; \r\n requestType: any; \r\n responseType: any;\r\n log?:boolean\r\n }>\r\n>(\r\n axiosInstance: AxiosInstance,\r\n actionsConfig: TActions\r\n): { [K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']> } {\r\n \r\n const actions = {} as any;\r\n\r\n for (const actionName in actionsConfig) {\r\n if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {\r\n const { method, endpoint , log} = actionsConfig[actionName];\r\n actions[actionName] = createAction(axiosInstance, method, endpoint);\r\n }\r\n }\r\n\r\n return actions;\r\n}","// file: src/core/globalStateManager.ts\r\n\r\nimport { ActionStateModule } from \"@/types\";\r\n\r\n\r\ntype CacheItem = {\r\n state: ActionStateModule<any>;\r\n listeners: Set<() => void>;\r\n};\r\n\r\nconst createInitialState = (): ActionStateModule<any> => ({\r\n data: null,\r\n links: undefined,\r\n meta: undefined,\r\n error: null,\r\n loading: false,\r\n success: false,\r\n called: false,\r\n isStale: false,\r\n message: undefined,\r\n validationErrors: [],\r\n rawResponse: null,\r\n});\r\n\r\nclass GlobalStateManager {\r\n private store = new Map<string, CacheItem>();\r\n\r\n /**\r\n * يحصل على لقطة (snapshot) للحالة الحالية لمفتاح معين.\r\n */\r\n public getSnapshot<T>(key: string): ActionStateModule<T> {\r\n return this.store.get(key)?.state as ActionStateModule<T> ?? createInitialState();\r\n }\r\n\r\n /**\r\n * يسجل دالة callback للاستماع إلى التغييرات على مفتاح معين.\r\n * @returns دالة لإلغاء الاشتراك.\r\n */\r\n public subscribe(key: string, callback: () => void): () => void {\r\n if (!this.store.has(key)) {\r\n this.store.set(key, { state: createInitialState(), listeners: new Set() });\r\n }\r\n const item = this.store.get(key)!;\r\n item.listeners.add(callback);\r\n\r\n return () => {\r\n item.listeners.delete(callback);\r\n };\r\n }\r\n\r\n /**\r\n * يحدّث الحالة لمفتاح معين ويقوم بإعلام جميع المشتركين.\r\n */\r\n public setState<T>(key: string, updater: (prevState: ActionStateModule<T>) => ActionStateModule<T>): void {\r\n const currentState = this.getSnapshot<T>(key);\r\n const newState = updater(currentState);\r\n\r\n // نضمن وجود العنصر قبل التحديث\r\n if (!this.store.has(key)) {\r\n this.store.set(key, { state: newState, listeners: new Set() });\r\n } else {\r\n this.store.get(key)!.state = newState;\r\n }\r\n\r\n // إعلام المشتركين\r\n this.store.get(key)!.listeners.forEach(listener => listener());\r\n }\r\n\r\n /**\r\n * يجعل البيانات المرتبطة بمفتاح معين \"قديمة\" (stale).\r\n */\r\n public invalidate(key: string): void {\r\n const state = this.getSnapshot(key);\r\n // يبطل فقط إذا كانت هناك بيانات بالفعل\r\n if (state.called) {\r\n this.setState(key, (prev) => ({ ...prev, isStale: true }));\r\n }\r\n }\r\n\r\n\r\n /**\r\n * [نسخة محدثة وأكثر قوة]\r\n * يجعل كل البيانات التي تبدأ بمفتاح معين \"قديمة\" (stale).\r\n * @example invalidateByPrefix('myModule/list::') سيبطل كل صفحات القائمة.\r\n */\r\n public invalidateByPrefix(prefix: string): void {\r\n this.store.forEach((value, key) => {\r\n if (key.startsWith(prefix)) {\r\n const state = this.getSnapshot(key);\r\n // يبطل فقط إذا كانت هناك بيانات بالفعل\r\n if (state.called) {\r\n this.setState(key, (prev) => ({ ...prev, isStale: true }));\r\n }\r\n }\r\n });\r\n }\r\n\r\n\r\n /**\r\n * Serializes the current state of the query store into a JSON string.\r\n * This is used on the server to pass the initial state to the client.\r\n * @returns A JSON string representing the dehydrated state.\r\n */\r\n public dehydrate(): string {\r\n const stateToPersist: Record<string, any> = {};\r\n this.store.forEach((value, key) => {\r\n // Dehydrate only if the state has been populated.\r\n if (value.state.called) {\r\n stateToPersist[key] = value.state;\r\n }\r\n });\r\n return JSON.stringify(stateToPersist);\r\n }\r\n\r\n /**\r\n * Merges a dehydrated state object into the current store.\r\n * This is used on the client to hydrate the state received from the server.\r\n * @param hydratedState - A JSON string from the `dehydrate` method.\r\n */\r\n public rehydrate(hydratedState: string): void {\r\n try {\r\n const parsedState = JSON.parse(hydratedState);\r\n for (const key in parsedState) {\r\n // We call setState to ensure any listening components are notified.\r\n // This prevents race conditions where a component might subscribe before rehydration is complete.\r\n this.setState(key, () => parsedState[key]);\r\n }\r\n } catch (e) {\r\n console.error(\"[api-core-lib] Failed to rehydrate state:\", e);\r\n }\r\n }\r\n\r\n \r\n}\r\n\r\nexport const globalStateManager = new GlobalStateManager();","interface CacheItem<T> {\r\n data: T;\r\n timestamp: number;\r\n duration: number; \r\n}\r\n\r\nexport class CacheManager {\r\n private cache = new Map<string, CacheItem<any>>();\r\n private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n set<T>(key: string, data: T, duration?: number): void {\r\n this.cache.set(key, {\r\n data,\r\n timestamp: Date.now(),\r\n duration: duration || this.defaultDuration,\r\n });\r\n }\r\n\r\n get<T>(key: string): T | null {\r\n const item = this.cache.get(key);\r\n if (!item) return null;\r\n\r\n const isExpired = Date.now() - item.timestamp > item.duration;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return null;\r\n }\r\n return item.data as T;\r\n }\r\n\r\n /**\r\n * [FIX] تم تحويلها إلى دالة عامة (generic) لتعيد النوع الصحيح.\r\n * الآن بدلًا من إرجاع CacheItem<unknown>، ستُرجع CacheItem<T>.\r\n */\r\n getWithMeta<T>(key: string): CacheItem<T> | null {\r\n const item = this.cache.get(key);\r\n if (!item) return null;\r\n\r\n const isExpired = Date.now() - item.timestamp > item.duration;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return null;\r\n }\r\n \r\n // نقوم بإخبار TypeScript أننا نثق بأن هذا العنصر هو من النوع T\r\n return item as CacheItem<T>;\r\n }\r\n\r\n delete(key: string): void {\r\n this.cache.delete(key);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n \r\n invalidateByPrefix(prefix: string): void {\r\n const keysToDelete: string[] = [];\r\n for (const key of this.cache.keys()) {\r\n if (key.startsWith(prefix)) {\r\n keysToDelete.push(key);\r\n }\r\n }\r\n keysToDelete.forEach(key => this.cache.delete(key));\r\n console.log(`Invalidated ${keysToDelete.length} cache entries with prefix: ${prefix}`);\r\n }\r\n}\r\n\r\nexport const cacheManager = new CacheManager();\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// interface CacheItem<T> {\r\n// data: T;\r\n// timestamp: number;\r\n// duration: number; \r\n// }\r\n\r\n// export class CacheManager {\r\n// private cache = new Map<string, CacheItem<any>>();\r\n// private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n// set<T>(key: string, data: T, duration?: number): void {\r\n// this.cache.set(key, {\r\n// data,\r\n// timestamp: Date.now(),\r\n// duration: duration || this.defaultDuration,\r\n// });\r\n// }\r\n\r\n// // هذه الدالة تبقى كما هي للتوافق مع الأجزاء الأخرى من المكتبة\r\n// get<T>(key: string): T | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key);\r\n// return null;\r\n// }\r\n// return item.data;\r\n// }\r\n\r\n// /**\r\n// * [دالة جديدة] تعيد عنصر الـ Cache بالكامل (البيانات + معلومات إضافية)\r\n// * دون حذفه عند انتهاء الصلاحية. هذا يسمح لنا بتطبيق منطق stale-while-revalidate.\r\n// */\r\n// getWithMeta<T>(key: string): CacheItem<T> | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// // لا نحذف العنصر حتى لو انتهت صلاحيته، فقط نرجعه\r\n// // المتلقي هو من يقرر ما سيفعله به\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key); // سنبقي على منطق الحذف للحفاظ على نظافة الكاش\r\n// return null;\r\n// }\r\n \r\n// return item as CacheItem<T>;\r\n// }\r\n\r\n// delete(key: string): void {\r\n// this.cache.delete(key);\r\n// }\r\n\r\n// clear(): void {\r\n// this.cache.clear();\r\n// }\r\n \r\n// /**\r\n// * [دالة جديدة] تقوم بإبطال (حذف) جميع عناصر الـ Cache التي تبدأ ببادئة معينة.\r\n// * أساسية لعملية invalidation التلقائية.\r\n// */\r\n// invalidateByPrefix(prefix: string): void {\r\n// const keysToDelete: string[] = [];\r\n// for (const key of this.cache.keys()) {\r\n// if (key.startsWith(prefix)) {\r\n// keysToDelete.push(key);\r\n// }\r\n// }\r\n// keysToDelete.forEach(key => this.cache.delete(key));\r\n// console.log(`Invalidated ${keysToDelete.length} cache entries with prefix: ${prefix}`);\r\n// }\r\n// }\r\n\r\n// // سنستخدم نفس النسخة الوحيدة (Singleton)\r\n// export const cacheManager = new CacheManager();\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// interface CacheItem<T> {\r\n// data: T;\r\n// timestamp: number;\r\n// duration: number;\r\n// }\r\n\r\n// export class CacheManager {\r\n// private cache = new Map<string, CacheItem<any>>();\r\n// private defaultDuration = 15 * 60 * 1000; // 15 minutes\r\n\r\n// set<T>(key: string, data: T, duration?: number): void {\r\n// this.cache.set(key, {\r\n// data,\r\n// timestamp: Date.now(),\r\n// duration: duration || this.defaultDuration,\r\n// });\r\n// }\r\n\r\n// get<T>(key: string): T | null {\r\n// const item = this.cache.get(key);\r\n// if (!item) return null;\r\n\r\n// const isExpired = Date.now() - item.timestamp > item.duration;\r\n// if (isExpired) {\r\n// this.cache.delete(key);\r\n// return null;\r\n// }\r\n// return item.data;\r\n// }\r\n\r\n// delete(key: string): void {\r\n// this.cache.delete(key);\r\n// }\r\n\r\n// clear(): void {\r\n// this.cache.clear();\r\n// }\r\n// }\r\n\r\n// export const cacheManager = new CacheManager();","// file: src/hooks/useApi.ts\r\n\r\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { createApiServices } from '../services/crud';\r\nimport { buildPaginateQuery } from '../core/utils'; // تأكد من استيراد الدالة المحدثة\r\nimport { UseApiConfig, QueryOptions, StandardResponse, ApiError, UseApiQuery, UseApiState } from '../types'; // تأكد من استيراد الأنواع من الملف الموحد\r\n\r\n// دالة مساعدة لبناء المسارات الديناميكية (إذا لم تكن في utils)\r\nfunction buildDynamicPath(template: string, params?: Record<string, string | number>): string {\r\n if (!params) return template;\r\n let path = template;\r\n for (const key in params) {\r\n path = path.replace(new RegExp(`{${key}}`, 'g'), String(params[key]));\r\n }\r\n return path;\r\n}\r\n\r\nexport function useApi<T>(\r\n axiosInstance: AxiosInstance,\r\n config: UseApiConfig<T>\r\n): any { // يمكنك تحديد نوع الإرجاع بشكل أدق إذا أردت\r\n const {\r\n endpoint,\r\n pathParams,\r\n // [REMOVE] لم نعد بحاجة لهذا الخيار\r\n // encodeQuery = true, \r\n initialData,\r\n initialQuery = {},\r\n enabled = true,\r\n refetchAfterChange = true,\r\n requestConfig,\r\n onSuccess,\r\n onError,\r\n } = config;\r\n\r\n const finalEndpoint = useMemo(\r\n () => buildDynamicPath(endpoint, pathParams),\r\n [endpoint, pathParams]\r\n );\r\n\r\n const [state, setState] = useState<UseApiState<T>>({\r\n data: initialData ?? null, rawResponse: null, loading: enabled, error: null,\r\n success: false, message: undefined, validationErrors: undefined\r\n });\r\n\r\n const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n\r\n const apiServices = useMemo(\r\n () => createApiServices<T>(axiosInstance, finalEndpoint),\r\n [axiosInstance, finalEndpoint]\r\n );\r\n\r\n const savedOnSuccess = useRef(onSuccess);\r\n const savedOnError = useRef(onError);\r\n\r\n useEffect(() => {\r\n savedOnSuccess.current = onSuccess;\r\n savedOnError.current = onError;\r\n }, [onSuccess, onError]);\r\n\r\n const fetchData = useCallback(async (currentQueryOptions?: QueryOptions) => {\r\n setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n try {\r\n const finalQuery = currentQueryOptions || queryOptions;\r\n const hasQueryParams = Object.keys(finalQuery).length > 0;\r\n\r\n // [FIX] تم تحديث الاستدعاء ليقبل وسيطًا واحدًا فقط\r\n const queryString = buildPaginateQuery(finalQuery);\r\n\r\n const result = hasQueryParams\r\n ? await apiServices.getWithQuery(queryString, requestConfig)\r\n : await apiServices.get(undefined, requestConfig);\r\n \r\n setState(result);\r\n\r\n if (result.success && savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Fetch successful!', result.data ?? undefined);\r\n } else if (!result.success && savedOnError.current) {\r\n savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n }\r\n } catch (e: any) {\r\n const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred.\" };\r\n setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n }\r\n }, [apiServices, queryOptions, requestConfig]);\r\n\r\n useEffect(() => {\r\n if (enabled) {\r\n fetchData();\r\n }\r\n }, [enabled, fetchData]);\r\n\r\n const handleActionResult = useCallback(async (actionPromise: Promise<StandardResponse<any>>) => {\r\n setState(prev => ({ ...prev, loading: true }));\r\n \r\n try {\r\n const result = await actionPromise;\r\n if (result.success) {\r\n if (savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Action successful!', result.data ?? undefined);\r\n }\r\n if (refetchAfterChange) {\r\n await fetchData(queryOptions);\r\n } else {\r\n setState(prev => ({ ...prev, ...result, loading: false })); // تحديث الحالة بالنتيجة إذا لم يكن هناك refetch\r\n }\r\n } else {\r\n if (savedOnError.current) {\r\n savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n }\r\n setState(prev => ({ ...prev, loading: false, error: result.error, success: false }));\r\n }\r\n return result;\r\n } catch(e: any) {\r\n const apiError: ApiError = { status: 500, message: e.message || \"An unexpected error occurred during action.\" };\r\n setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n return { data: null, error: apiError, loading: false, success: false, rawResponse: e } as StandardResponse<any>;\r\n }\r\n }, [refetchAfterChange, fetchData, queryOptions]);\r\n\r\n const actions = {\r\n fetch: fetchData,\r\n create: (newItem: any, options?: any) => handleActionResult(apiServices.post(newItem, options)),\r\n put: (id: any, item: any, options?: any) => handleActionResult(apiServices.put(id, item, options)),\r\n update: (id: any, updatedItem: any, options?: any) => handleActionResult(apiServices.patch(id, updatedItem, options)),\r\n remove: (id: any, options?: any) => handleActionResult(apiServices.remove(id, options)),\r\n bulkRemove: (ids: any, options?: any) => handleActionResult(apiServices.bulkDelete(ids, options)),\r\n };\r\n\r\n const query: UseApiQuery = {\r\n options: queryOptions,\r\n setOptions: setQueryOptions,\r\n setPage: (page: number) => setQueryOptions(prev => ({ ...prev, page })),\r\n setLimit: (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit })),\r\n setSearchTerm: (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search })),\r\n setSorting: (sortBy: any) => setQueryOptions(prev => ({ ...prev, sortBy })),\r\n setFilters: (filter: any) => setQueryOptions(prev => ({ ...prev, page: 1, filter })),\r\n setQueryParam: (key: any, value: any) => setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : value })),\r\n reset: () => setQueryOptions(initialQuery),\r\n };\r\n\r\n return { state, setState, actions, query };\r\n}\r\n\r\n\r\n\r\n\r\n// // file: src/hooks/useApi.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { createApiServices } from '../services/crud';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { UseApiConfig, QueryOptions, StandardResponse, ApiError } from '../types';\r\n// import { UseApiActions, UseApiQuery, UseApiReturn, UseApiState } from '../types/useApi.types';\r\n\r\n// export function useApi<T>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ): UseApiReturn<T> { \r\n// const {\r\n// endpoint, initialData, initialQuery = {}, enabled = true,\r\n// refetchAfterChange = true, requestConfig, onSuccess, onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<UseApiState<T>>({\r\n// data: initialData ?? null, rawResponse: null, loading: enabled, error: null,\r\n// success: false, message: undefined, validationErrors: undefined\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n// const savedOnSuccess = useRef(onSuccess);\r\n// const savedOnError = useRef(onError);\r\n\r\n// useEffect(() => {\r\n// savedOnSuccess.current = onSuccess;\r\n// savedOnError.current = onError;\r\n// }, [onSuccess, onError]);\r\n\r\n// const fetchData = useCallback(async () => {\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// try {\r\n// const hasQueryParams = Object.keys(queryOptions).length > 0;\r\n// const queryString = buildPaginateQuery(queryOptions);\r\n\r\n// const result = hasQueryParams\r\n// ? await apiServices.getWithQuery(queryString, requestConfig)\r\n// : await apiServices.get(undefined, requestConfig);\r\n \r\n// setState(result);\r\n\r\n// if (result.success && savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Fetch successful!', result.data ?? undefined);\r\n// } else if (!result.success && savedOnError.current) {\r\n// savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// } catch (e: any) {\r\n// console.error(\"[useApi Fetch Error] An unhandled exception occurred:\", e);\r\n// const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred.\" };\r\n// setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// }\r\n// }, [apiServices, queryOptions, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// }, [enabled, fetchData]);\r\n\r\n// const handleActionResult = useCallback(async (actionPromise: Promise<StandardResponse<any>>) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n \r\n// // ✅ [الإصلاح الرئيسي 2]: التعامل مع الوعد نفسه، وليس فقط نتيجته\r\n// // هذا يضمن أننا نلتقط الأخطاء حتى لو لم تُرجع `StandardResponse`\r\n// try {\r\n// const result = await actionPromise;\r\n\r\n// if (result.success) {\r\n// if (savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Action successful!', result.data ?? undefined);\r\n// }\r\n// if (refetchAfterChange) {\r\n// setQueryOptions(prev => ({ ...prev }));\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false }));\r\n// }\r\n// } else {\r\n// if (savedOnError.current) {\r\n// savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n// setState(prev => ({ ...prev, loading: false, error: result.error, success: false }));\r\n// }\r\n// return result;\r\n\r\n// } catch(e: any) {\r\n// console.error(\"[useApi Action Error] An unhandled exception occurred:\", e);\r\n// const apiError: ApiError = { status: 500, message: e.message || \"An unexpected client-side error occurred during action.\" };\r\n// setState(prev => ({ ...prev, loading: false, error: apiError, success: false }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// // إرجاع استجابة خطأ موحدة\r\n// return { data: null, error: apiError, loading: false, success: false, rawResponse: e } as StandardResponse<any>;\r\n// }\r\n// }, [refetchAfterChange]);\r\n\r\n// const actions: UseApiActions<T> = {\r\n// fetch: fetchData,\r\n// create: (newItem, options) => handleActionResult(apiServices.post(newItem as any, options)),\r\n// put: (id, item, options) => handleActionResult(apiServices.put(id, item as any, options)),\r\n// update: (id, updatedItem, options) => handleActionResult(apiServices.patch(id, updatedItem as any, options)),\r\n// remove: (id, options) => handleActionResult(apiServices.remove(id, options)),\r\n// bulkRemove: (ids, options) => handleActionResult(apiServices.bulkDelete(ids, options)),\r\n// };\r\n\r\n// const query: UseApiQuery = {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage: (page: number) => setQueryOptions(prev => ({ ...prev, page })),\r\n// setLimit: (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit })),\r\n// setSearchTerm: (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search })),\r\n// setSorting: (sortBy) => setQueryOptions(prev => ({ ...prev, sortBy })),\r\n// setFilters: (filter) => setQueryOptions(prev => ({ ...prev, page: 1, filter })),\r\n// setQueryParam: (key, value) => setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : value })),\r\n// reset: () => setQueryOptions(initialQuery),\r\n// };\r\n\r\n// return { state, setState, actions, query };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { createApiServices } from '../services/crud';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { UseApiConfig, QueryOptions, ActionOptions, StandardResponse } from '../types';\r\n// import { UseApiActions, UseApiQuery, UseApiReturn, UseApiState } from './useApi.types';\r\n\r\n\r\n// // The generic constraint ensures that the data type has an 'id' property.\r\n// export function useApi<T extends { id?: string | number }>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ): UseApiReturn<T> { \r\n// const {\r\n// endpoint,\r\n// initialData,\r\n// initialQuery = { limit: 10 },\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<UseApiState<T>>({\r\n// data: initialData ?? null,\r\n// rawResponse: null,\r\n// loading: enabled,\r\n// error: null,\r\n// success: false,\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// const fetch = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// // Set loading state but keep previous data for a better UX during refetch\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// const fetchData = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// // Set loading state but keep previous data for a better UX during refetch\r\n// setState(prev => ({ ...prev, loading: true, error: null }));\r\n \r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// // fetchData is memoized, so this effect is safe.\r\n// }, [enabled, queryOptions, fetchData]);\r\n\r\n// // --- Actions ---\r\n// // We define a helper to reduce repetition in action handlers\r\n// const handleActionResult = async (\r\n// actionPromise: Promise<StandardResponse<any>>,\r\n// successMessage: string,\r\n// errorMessage: string\r\n// ) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await actionPromise;\r\n// if (result.success) {\r\n// if (refetchAfterChange) {\r\n// // We don't need to wait for fetchData, it will update state when it's done.\r\n// fetchData();\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false }));\r\n// }\r\n// if (onSuccess) onSuccess(result.message || successMessage, result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || errorMessage, result.error ?? undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const createItem: UseApiActions<T>['create'] = (newItem, options) => {\r\n// return handleActionResult(\r\n// apiServices.post(newItem, options),\r\n// 'Item created successfully!',\r\n// 'Create failed'\r\n// );\r\n// };\r\n \r\n// const putItem: UseApiActions<T>['put'] = (id, item, options) => {\r\n// return handleActionResult(\r\n// apiServices.put(String(id), item, options),\r\n// 'Item replaced successfully!',\r\n// 'Replace failed'\r\n// );\r\n// };\r\n\r\n// const updateItem: UseApiActions<T>['update'] = (id, updatedItem, options) => {\r\n// return handleActionResult(\r\n// apiServices.patch(String(id), updatedItem, options),\r\n// 'Item updated successfully!',\r\n// 'Update failed'\r\n// );\r\n// };\r\n\r\n// const deleteItem: UseApiActions<T>['remove'] = (id, options) => {\r\n// return handleActionResult(\r\n// apiServices.remove(String(id), options),\r\n// 'Item deleted successfully!',\r\n// 'Delete failed'\r\n// );\r\n// };\r\n \r\n// const bulkDeleteItem: UseApiActions<T>['bulkRemove'] = (ids, options) => {\r\n// return handleActionResult(\r\n// apiServices.bulkDelete(ids.map(String), options),\r\n// 'Items deleted successfully!',\r\n// 'Bulk delete failed'\r\n// );\r\n// };\r\n\r\n// const uploadFile: UseApiActions<T>['upload'] = (file, additionalData, options) => {\r\n// return handleActionResult(\r\n// apiServices.upload(file, additionalData, options),\r\n// 'File uploaded successfully!',\r\n// 'Upload failed'\r\n// );\r\n// };\r\n\r\n// // --- Query Controls ---\r\n// const setPage = (page: number) => setQueryOptions(prev => ({ ...prev, page }));\r\n// const setLimit = (limit: number) => setQueryOptions(prev => ({ ...prev, page: 1, limit }));\r\n// const setSearchTerm = (search: string) => setQueryOptions(prev => ({ ...prev, page: 1, search }));\r\n// const setSorting = (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setQueryOptions(prev => ({ ...prev, sortBy }));\r\n// const setFilters = (filter: Record<string, any>) => setQueryOptions(prev => ({ ...prev, page: 1, filter }));\r\n// const setQueryParam = (key: string, value: any) => {\r\n// setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? prev.page : value }));\r\n// };\r\n// const resetQuery = () => setQueryOptions(initialQuery);\r\n \r\n// // Assemble the final return object, ensuring it matches the UseApiReturn type.\r\n// const actions: UseApiActions<T> = {\r\n// // refetch: fetchData,\r\n// fetch: fetchData,\r\n// create: createItem,\r\n// put: putItem,\r\n// update: updateItem,\r\n// remove: deleteItem,\r\n// bulkRemove: bulkDeleteItem,\r\n// upload: uploadFile,\r\n// };\r\n\r\n// const query: UseApiQuery = {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage,\r\n// setLimit,\r\n// setSearchTerm,\r\n// setSorting,\r\n// setFilters,\r\n// setQueryParam,\r\n// reset: resetQuery,\r\n// };\r\n\r\n// return {\r\n// state,\r\n// setState,\r\n// actions,\r\n// query, // Fixed typo from 'querry' to 'query'\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // في ملف: useApi.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef } from 'react';\r\n// import { createApiServices } from '../services/crud';\r\n// import { ApiError, QueryOptions, StandardResponse, UseApiConfig, ActionOptions } from '../types';\r\n// import { buildPaginateQuery } from '../core/utils';\r\n// import { AxiosInstance } from 'axios';\r\n\r\n// export function useApi<T extends { id?: string | number }>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiConfig<T>\r\n// ) {\r\n// const {\r\n// endpoint,\r\n// initialData,\r\n// initialQuery = { limit: 10 },\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// const [state, setState] = useState<StandardResponse<T | T[]>>({\r\n// data: initialData || null,\r\n// rawResponse: null,\r\n// loading: enabled,\r\n// error: null,\r\n// success: false,\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<QueryOptions>(initialQuery);\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// // --- دالة جلب البيانات ---\r\n// const fetchData = useCallback(async (options?: QueryOptions) => {\r\n// const currentQuery = options || queryOptions;\r\n// setState(prev => ({ ...prev, data: null, loading: true, error: null }));\r\n// const queryString = buildPaginateQuery(currentQuery);\r\n// const result = await apiServices.getWithQuery(queryString, {\r\n// cancelTokenKey: endpoint,\r\n// ...requestConfig\r\n// });\r\n \r\n// setState(result as StandardResponse<T | T[]>);\r\n\r\n// if (!result.success && onError) {\r\n// onError(result.message || 'Fetch failed', result.error || undefined);\r\n// }\r\n// }, [apiServices, queryOptions, endpoint, onError, requestConfig]);\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchData();\r\n// }\r\n// // eslint-disable-next-line react-hooks/exhaustive-deps\r\n// }, [enabled, queryOptions]);\r\n\r\n// // --- دوال الأكشن (Actions) ---\r\n// const createItem = async (newItem: Partial<T>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.post(newItem, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item created successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Create failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة putItem (لعمليات PUT)\r\n// */\r\n// const putItem = async (id: string, item: T, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.put(id, item, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item replaced successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Replace failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const updateItem = async (id: string, updatedItem: Partial<T>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.patch(id, updatedItem, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item updated successfully!', result.data as T);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Update failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// const deleteItem = async (id: string, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.remove(id, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Item deleted successfully!');\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Delete failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة للحذف الجماعي\r\n// */\r\n// const bulkDeleteItem = async (ids: string[], options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.bulkDelete(ids, options);\r\n// if (result.success) {\r\n// if (refetchAfterChange) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'Items deleted successfully!');\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Bulk delete failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n \r\n// /**\r\n// * ✅ NEW: إضافة دالة لرفع الملفات\r\n// * لا تقوم بإعادة جلب البيانات افتراضيًا لأن استجابة الرفع قد تكون مختلفة.\r\n// */\r\n// const uploadFile = async (file: File, additionalData?: Record<string, any>, options?: ActionOptions) => {\r\n// setState(prev => ({ ...prev, loading: true }));\r\n// const result = await apiServices.upload(file, additionalData, options);\r\n// if (result.success) {\r\n// // عادة لا نحتاج لإعادة الجلب هنا، لكن يمكن التحكم في ذلك عبر `refetchAfterChange`\r\n// if (refetchAfterChange && options?.refetch !== false) await fetchData();\r\n// else setState(prev => ({ ...prev, loading: false }));\r\n// if (onSuccess) onSuccess(result.message || 'File uploaded successfully!', result.data);\r\n// } else {\r\n// setState(prev => ({ ...prev, loading: false, error: result.error }));\r\n// if (onError) onError(result.message || 'Upload failed', result.error || undefined);\r\n// }\r\n// return result;\r\n// };\r\n\r\n// // --- دوال التحكم في الاستعلام ---\r\n// const setPage = (page: number) => setQueryOptions(prev => ({ ...prev, page }));\r\n// const setLimit = (limit: number) => setQueryOptions(prev => ({ ...prev, limit, page: 1 }));\r\n// const setSearchTerm = (search: string) => setQueryOptions(prev => ({ ...prev, search, page: 1 }));\r\n// const setSorting = (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setQueryOptions(prev => ({ ...prev, sortBy }));\r\n// const setFilters = (filter: Record<string, any>) => setQueryOptions(prev => ({ ...prev, filter, page: 1 }));\r\n// const setQueryParam = (key: string, value: any) => {\r\n// setQueryOptions(prev => ({ ...prev, [key]: value, page: key !== 'page' ? 1 : prev.page }));\r\n// };\r\n// const resetQuery = () => setQueryOptions(initialQuery);\r\n\r\n// return {\r\n// state,\r\n// setState,\r\n// actions: {\r\n// fetch: fetchData,\r\n// create: createItem,\r\n// put: putItem, // <-- NEW\r\n// update: updateItem,\r\n// remove: deleteItem,\r\n// bulkRemove: bulkDeleteItem, // <-- NEW\r\n// upload: uploadFile, // <-- NEW\r\n// },\r\n// querry: {\r\n// options: queryOptions,\r\n// setOptions: setQueryOptions,\r\n// setPage,\r\n// setLimit,\r\n// setSearchTerm,\r\n// setSorting,\r\n// setFilters,\r\n// setQueryParam,\r\n// reset: resetQuery,\r\n// },\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","// file: src/hooks/useApiRecord.ts\r\n\r\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { UseApiRecordActions, UseApiRecordConfig, UseApiRecordReturn, UseApiRecordState } from '@/types/useApiRecord.types';\r\nimport { createApiServices } from '@/services/crud';\r\nimport { ActionOptions, ApiError, StandardResponse } from '@/types';\r\n\r\n/**\r\n * A helper function to build a dynamic URL path by replacing placeholders.\r\n * @param template The URL template with placeholders (e.g., 'v1/users/{userId}').\r\n * @param params An object with keys matching the placeholders.\r\n * @returns The final, resolved URL path.\r\n */\r\nfunction buildDynamicPath(template: string, params?: Record<string, string | number>): string {\r\n if (!params) return template;\r\n let path = template;\r\n for (const key in params) {\r\n path = path.replace(new RegExp(`{${key}}`, 'g'), String(params[key]));\r\n }\r\n return path;\r\n}\r\n\r\n/**\r\n * A React hook for managing the lifecycle of a single API resource (a record).\r\n * It handles fetching, updating, replacing, and deleting a record, while managing\r\n * loading, error, and data states. It supports dynamic path parameters for flexible API routing.\r\n *\r\n * @template T The data type of the record being managed.\r\n * @param {AxiosInstance} axiosInstance - The configured Axios instance for making API calls.\r\n * @param {UseApiRecordConfig<T>} config - Configuration options for the hook.\r\n * @returns {UseApiRecordReturn<T>} An object containing the state, actions, and setState function.\r\n */\r\nexport function useApiRecord<T>(\r\n axiosInstance: AxiosInstance,\r\n config: UseApiRecordConfig<T>\r\n): UseApiRecordReturn<T> {\r\n const {\r\n endpoint,\r\n pathParams,\r\n recordId,\r\n initialData,\r\n enabled = true,\r\n refetchAfterChange = true,\r\n requestConfig,\r\n onSuccess,\r\n onError,\r\n } = config;\r\n\r\n // [STABILITY] Memoize the final endpoint to make it a stable dependency.\r\n // It will only be recalculated if the template or path parameters change.\r\n const finalEndpoint = useMemo(\r\n () => buildDynamicPath(endpoint, pathParams),\r\n [endpoint, pathParams]\r\n );\r\n\r\n // [STABILITY] Memoize initialState to prevent re-creating it on every render,\r\n // which is crucial for preventing infinite loops in hooks that depend on it.\r\n const initialState = useMemo<UseApiRecordState<T>>(() => ({\r\n data: initialData ?? null,\r\n rawResponse: null,\r\n error: null,\r\n loading: enabled && !!recordId,\r\n success: false,\r\n message: undefined,\r\n validationErrors: undefined,\r\n }), [initialData, enabled, recordId]);\r\n\r\n const [state, setState] = useState<UseApiRecordState<T>>(initialState);\r\n\r\n // Use refs for callbacks to prevent stale closures.\r\n const savedOnSuccess = useRef(onSuccess);\r\n const savedOnError = useRef(onError);\r\n\r\n useEffect(() => {\r\n savedOnSuccess.current = onSuccess;\r\n savedOnError.current = onError;\r\n }, [onSuccess, onError]);\r\n\r\n // [STABILITY] Memoize the apiServices instance. It will only be recreated if\r\n // the Axios instance or the final endpoint URL changes.\r\n const apiServices = useMemo(\r\n () => createApiServices<T>(axiosInstance, finalEndpoint),\r\n [axiosInstance, finalEndpoint]\r\n );\r\n\r\n /**\r\n * Fetches the record from the API. This function is memoized and stable.\r\n */\r\n const fetchRecord = useCallback(async () => {\r\n if (!recordId) {\r\n setState(initialState);\r\n return;\r\n }\r\n\r\n setState((prev) => ({ ...prev, loading: true, error: null, success: false }));\r\n\r\n try {\r\n const result = await apiServices.get(recordId, requestConfig);\r\n setState(result);\r\n\r\n if (result.success && savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Record fetched successfully!', result.data ?? undefined);\r\n } else if (!result.success && savedOnError.current) {\r\n savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n }\r\n } catch (e: any) {\r\n console.error('[useApiRecord Fetch Error]', e);\r\n const apiError: ApiError = {\r\n status: e.response?.status || 500,\r\n message: e.message || 'An unexpected client-side error occurred.',\r\n code: e.code,\r\n };\r\n setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n }\r\n }, [apiServices, recordId, requestConfig, initialState]);\r\n\r\n // Effect to trigger the initial data fetch.\r\n useEffect(() => {\r\n if (enabled) {\r\n fetchRecord();\r\n }\r\n }, [enabled, fetchRecord]); // `fetchRecord` is a stable dependency.\r\n\r\n /**\r\n * A centralized handler for all mutation actions (update, put, remove).\r\n */\r\n const handleActionResult = useCallback(async <R extends T | null>(\r\n actionPromise: Promise<StandardResponse<R>>,\r\n options?: ActionOptions\r\n ): Promise<StandardResponse<R>> => {\r\n setState((prev) => ({ ...prev, loading: true }));\r\n\r\n try {\r\n const result = await actionPromise;\r\n\r\n if (result.success) {\r\n if (savedOnSuccess.current) {\r\n savedOnSuccess.current(result.message || 'Action successful!', result.data);\r\n }\r\n const shouldRefetch = options?.refetch ?? refetchAfterChange;\r\n if (shouldRefetch) {\r\n await fetchRecord();\r\n } else {\r\n // Optimistic update: set state directly from the action's response.\r\n setState({ ...result, loading: false, data: result.data });\r\n }\r\n } else {\r\n if (savedOnError.current) {\r\n savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n }\r\n setState((prev) => ({ ...prev, ...result, loading: false }));\r\n }\r\n return result;\r\n } catch (e: any) {\r\n console.error('[useApiRecord Action Error]', e);\r\n const apiError: ApiError = {\r\n status: e.response?.status || 500,\r\n message: e.message || 'An unexpected error occurred during the action.',\r\n code: e.code,\r\n };\r\n const errorResponse: StandardResponse<R> = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };\r\n setState(errorResponse as UseApiRecordState<T>);\r\n if (savedOnError.current) {\r\n savedOnError.current(apiError.message, apiError);\r\n }\r\n return errorResponse;\r\n }\r\n },\r\n [refetchAfterChange, fetchRecord, initialState]\r\n );\r\n\r\n const actions: UseApiRecordActions<T> = {\r\n fetch: fetchRecord,\r\n update: (updatedItem: Partial<T>, options?: ActionOptions) =>\r\n handleActionResult(apiServices.patch(recordId!, updatedItem, { ...requestConfig, ...options }), options),\r\n put: (item: T, options?: ActionOptions) =>\r\n handleActionResult(apiServices.put(recordId!, item, { ...requestConfig, ...options }), options),\r\n remove: (options?: ActionOptions) =>\r\n handleActionResult(apiServices.remove(recordId!, { ...requestConfig, ...options }), options),\r\n resetState: () => setState(initialState),\r\n };\r\n\r\n return { state, setState, actions };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// // file: src/hooks/useApiRecord.ts\r\n\r\n// import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; // <-- أضفنا useMemo\r\n// import { AxiosInstance } from 'axios';\r\n// import { UseApiRecordActions, UseApiRecordConfig, UseApiRecordReturn, UseApiRecordState } from '@/types/useApiRecord.types';\r\n// import { createApiServices } from '@/services/crud';\r\n// import { ActionOptions, ApiError, StandardResponse } from '@/types';\r\n\r\n// /**\r\n// * A React hook for managing the lifecycle of a single API resource (a record).\r\n// * It handles fetching, updating, replacing, and deleting a record, while managing\r\n// * loading, error, and data states.\r\n// */\r\n// export function useApiRecord<T>(\r\n// axiosInstance: AxiosInstance,\r\n// config: UseApiRecordConfig<T>\r\n// ): UseApiRecordReturn<T> {\r\n// const {\r\n// endpoint,\r\n// recordId,\r\n// initialData,\r\n// enabled = true,\r\n// refetchAfterChange = true,\r\n// requestConfig,\r\n// onSuccess,\r\n// onError,\r\n// } = config;\r\n\r\n// // [FIX] Memoize initialState to make it stable across re-renders.\r\n// // It will only be recreated if the initialData prop changes.\r\n// const initialState = useMemo<UseApiRecordState<T>>(() => ({\r\n// data: initialData ?? null,\r\n// rawResponse: null,\r\n// error: null,\r\n// loading: enabled && !!recordId,\r\n// success: false,\r\n// message: undefined,\r\n// validationErrors: undefined,\r\n// }), [initialData, enabled, recordId]); // Dependencies for initialState itself\r\n\r\n// const [state, setState] = useState<UseApiRecordState<T>>(initialState);\r\n\r\n// const savedOnSuccess = useRef(onSuccess);\r\n// const savedOnError = useRef(onError);\r\n\r\n// useEffect(() => {\r\n// savedOnSuccess.current = onSuccess;\r\n// savedOnError.current = onError;\r\n// }, [onSuccess, onError]);\r\n\r\n// const apiServices = useRef(createApiServices<T>(axiosInstance, endpoint)).current;\r\n\r\n// const fetchRecord = useCallback(async () => {\r\n// if (!recordId) {\r\n// // Use the memoized initialState for resetting\r\n// setState(initialState);\r\n// return;\r\n// }\r\n\r\n// setState((prev) => ({ ...prev, loading: true, error: null, success: false }));\r\n\r\n// try {\r\n// const result = await apiServices.get(recordId, requestConfig);\r\n// setState(result);\r\n\r\n// if (result.success && savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Record fetched successfully!', result.data ?? undefined);\r\n// } else if (!result.success && savedOnError.current) {\r\n// savedOnError.current(result.message || 'Fetch failed', result.error ?? undefined);\r\n// }\r\n// } catch (e: any) {\r\n// console.error('[useApiRecord Fetch Error]', e);\r\n// const apiError: ApiError = {\r\n// status: e.response?.status || 500,\r\n// message: e.message || 'An unexpected client-side error occurred.',\r\n// code: e.code,\r\n// };\r\n// setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// }\r\n// // [FIX] Removed unstable `initialState` from dependency array.\r\n// // Also, `requestConfig` can be unstable if defined as an object literal in the parent component.\r\n// // Advise users to memoize `requestConfig` if it's complex.\r\n// }, [apiServices, recordId, requestConfig, initialState]); // initialState is now stable due to useMemo\r\n\r\n// useEffect(() => {\r\n// if (enabled) {\r\n// fetchRecord();\r\n// }\r\n// }, [enabled, fetchRecord]); // Removed recordId as it's already a dependency of fetchRecord\r\n\r\n// const handleActionResult = useCallback(\r\n// async <R extends T | null>(\r\n// actionPromise: Promise<StandardResponse<R>>,\r\n// options?: ActionOptions\r\n// ): Promise<StandardResponse<R>> => {\r\n// setState((prev) => ({ ...prev, loading: true }));\r\n\r\n// try {\r\n// const result = await actionPromise;\r\n\r\n// if (result.success) {\r\n// if (savedOnSuccess.current) {\r\n// savedOnSuccess.current(result.message || 'Action successful!', result.data);\r\n// }\r\n// const shouldRefetch = options?.refetch ?? refetchAfterChange;\r\n// if (shouldRefetch) {\r\n// await fetchRecord();\r\n// } else {\r\n// setState({ ...result, loading: false, data: result.data });\r\n// }\r\n// } else {\r\n// if (savedOnError.current) {\r\n// savedOnError.current(result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n// setState((prev) => ({ ...prev, ...result, loading: false }));\r\n// }\r\n// return result;\r\n// } catch (e: any) {\r\n// console.error('[useApiRecord Action Error]', e);\r\n// const apiError: ApiError = {\r\n// status: e.response?.status || 500,\r\n// message: e.message || 'An unexpected error occurred during the action.',\r\n// code: e.code,\r\n// };\r\n// // Use the stable initialState for the error response structure\r\n// const errorResponse: StandardResponse<R> = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };\r\n// setState(errorResponse as UseApiRecordState<T>);\r\n// if (savedOnError.current) {\r\n// savedOnError.current(apiError.message, apiError);\r\n// }\r\n// return errorResponse;\r\n// }\r\n// },\r\n// // [FIX] Removed unstable dependencies. `fetchRecord` is now stable.\r\n// [refetchAfterChange, fetchRecord, initialState]\r\n// );\r\n\r\n// const actions: UseApiRecordActions<T> = {\r\n// fetch: fetchRecord,\r\n// update: (updatedItem: Partial<T>, options?: ActionOptions) =>\r\n// handleActionResult(apiServices.patch(recordId!, updatedItem, { ...requestConfig, ...options }), options),\r\n// put: (item: T, options?: ActionOptions) =>\r\n// handleActionResult(apiServices.put(recordId!, item, { ...requestConfig, ...options }), options),\r\n// remove: (options?: ActionOptions) =>\r\n// handleActionResult(apiServices.remove(recordId!, { ...requestConfig, ...options }), options),\r\n// resetState: () => setState(initialState),\r\n// };\r\n\r\n// return { state, setState, actions };\r\n// }","import { useEffect, useRef } from 'react';\r\nimport isEqual from 'fast-deep-equal';\r\n\r\ntype EffectCallback = () => (void | (() => void));\r\ntype DependencyList = readonly any[];\r\n\r\nexport function useDeepCompareEffect(callback: EffectCallback, dependencies: DependencyList) {\r\n const currentDependenciesRef = useRef<DependencyList>();\r\n\r\n if (!isEqual(currentDependenciesRef.current, dependencies)) {\r\n currentDependenciesRef.current = dependencies;\r\n }\r\n\r\n useEffect(callback, [currentDependenciesRef.current]);\r\n}","\"use client\";\r\n// file: src/hooks/useApiModule/useApiModule.ts\r\n\r\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\r\nimport { AxiosInstance } from 'axios';\r\nimport { globalStateManager } from '../../core/globalStateManager';\r\nimport { callDynamicApi } from '../../services/crud';\r\nimport { \r\n ApiModuleConfig, UseApiModuleOptions, UseApiModuleReturn, ModuleStates, ModuleActions,\r\n ExecuteOptions, InputOf, OutputOf, ActionConfigModule, ActionState\r\n} from '../../types/apiModule.types';\r\nimport { QueryOptions, StandardResponse, UseApiQuery } from '../../types';\r\n\r\nconst ApiModuleContext = createContext<UseApiModuleReturn<any> | null>(null);\r\nexport const ApiModuleProvider = ApiModuleContext.Provider;\r\n\r\n\r\n\r\nexport function useModuleContext<TModule extends ApiModuleConfig<any>>(): UseApiModuleReturn<TModule['actions']> {\r\n const context = useContext(ApiModuleContext);\r\n if (!context) {\r\n throw new Error('useModuleContext must be used within an ApiModuleProvider');\r\n }\r\n // نحن نقوم بعمل cast آمن هنا، لأننا نثق أن المطور سيستخدم الـ Provider الصحيح\r\n return context as UseApiModuleReturn<TModule['actions']>;\r\n}\r\n\r\nconst generateCacheKey = (\r\n moduleName: string,\r\n actionName: string,\r\n input?: unknown,\r\n callOptions: { pathParams?: Record<string, any> } = {}\r\n): string => {\r\n const params = { path: callOptions.pathParams, body: input };\r\n try {\r\n return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n } catch (error) {\r\n return `${moduleName}/${actionName}::${Date.now()}`;\r\n }\r\n};\r\n\r\nexport function useApiModule<TActions extends Record<string, ActionConfigModule<any, any>>>(\r\n axiosInstance: AxiosInstance,\r\n moduleConfig: ApiModuleConfig<TActions>,\r\n options: UseApiModuleOptions = {}\r\n): UseApiModuleReturn<TActions> {\r\n const { refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams, enabled = true, hydratedState } = options;\r\n\r\n const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n const savedCallbacks = useRef({ onSuccess, onError });\r\n useEffect(() => { savedCallbacks.current = { onSuccess, onError }; }, [onSuccess, onError]);\r\n\r\n useMemo(() => {\r\n if (hydratedState) {\r\n globalStateManager.rehydrate(hydratedState);\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [hydratedState]);\r\n\r\n const actions = useMemo<ModuleActions<TActions>>(() => {\r\n return (Object.keys(moduleConfig.actions) as Array<keyof TActions>).reduce((acc, actionName) => {\r\n const actionConfig = moduleConfig.actions[actionName];\r\n \r\n const execute = async <TContext = unknown>(\r\n input?: InputOf<typeof actionConfig>,\r\n options: ExecuteOptions<InputOf<typeof actionConfig>, OutputOf<typeof actionConfig>, TContext> = {}\r\n ): Promise<StandardResponse<OutputOf<typeof actionConfig>>> => {\r\n const finalPathParams = { ...(JSON.parse(pathParamsString)), ...options.pathParams };\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams: finalPathParams });\r\n let mutationContext: TContext | undefined;\r\n\r\n try {\r\n if (options.onMutate) {\r\n mutationContext = await options.onMutate(input as InputOf<typeof actionConfig>);\r\n }\r\n\r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, loading: true, called: true, error: null, isStale: false }));\r\n \r\n const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n pathParams: finalPathParams, body: input, config: options.config,\r\n });\r\n \r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...result, loading: false }));\r\n \r\n if (result.success) {\r\n savedCallbacks.current.onSuccess?.(actionName as string, result.message || 'Action successful', result.data);\r\n options.onSuccess?.(result.data, mutationContext);\r\n \r\n actionConfig.invalidates?.forEach((keyToInvalidate: string) => {\r\n const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;\r\n globalStateManager.invalidateByPrefix(prefix);\r\n });\r\n } else {\r\n savedCallbacks.current.onError?.(actionName as string, result.message || 'Action failed', result.error ?? undefined);\r\n options.onError?.(result.error!, mutationContext);\r\n }\r\n return result;\r\n } catch (error: any) {\r\n const apiError = { status: 500, message: error.message };\r\n const errorResult = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...errorResult, loading: false }));\r\n \r\n savedCallbacks.current.onError?.(actionName as string, apiError.message, apiError);\r\n options.onError?.(apiError, mutationContext);\r\n return errorResult;\r\n }\r\n };\r\n\r\n const reset = (input?: InputOf<typeof actionConfig>, options: { pathParams?: Record<string, any> } = {}) => {\r\n const finalPathParams = { ...(JSON.parse(pathParamsString)), ...options.pathParams };\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams: finalPathParams });\r\n globalStateManager.setState(cacheKey, () => ({\r\n data: null, error: null, loading: false, success: false,\r\n called: false, isStale: false, rawResponse: null\r\n }));\r\n };\r\n\r\n acc[actionName] = { execute, reset };\r\n return acc;\r\n }, {} as ModuleActions<TActions>);\r\n }, [axiosInstance, moduleConfig, pathParamsString]);\r\n\r\n const queries = useMemo(() => {\r\n const builtQueries: any = {};\r\n for (const actionName in moduleConfig.actions) {\r\n if (moduleConfig.actions[actionName]?.hasQuery) {\r\n const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n };\r\n builtQueries[actionName] = {\r\n options: queryOptions[actionName] || {}, setOptions: setActionQueryOptions,\r\n setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n setFilters: (filter: Record<string, unknown>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n setQueryParam: (key: string, value: unknown) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? value : p.page })),\r\n reset: () => setActionQueryOptions({}),\r\n };\r\n }\r\n }\r\n return builtQueries as UseApiModuleReturn<TActions>['queries'];\r\n }, [queryOptions, moduleConfig.actions]);\r\n \r\n const states = useMemo<ModuleStates<TActions>>(() => {\r\n const finalStates: any = {};\r\n for (const actionName of Object.keys(moduleConfig.actions) as Array<keyof TActions>) {\r\n Object.defineProperty(finalStates, actionName, {\r\n get: () => {\r\n const actionConfig = moduleConfig.actions[actionName];\r\n const query = queries[actionName as keyof typeof queries]?.options;\r\n const input: unknown = actionConfig.hasQuery ? query : undefined;\r\n \r\n const pathParams = JSON.parse(pathParamsString);\r\n const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName as string, input, { pathParams });\r\n \r\n const state = useSyncExternalStore(\r\n (callback) => globalStateManager.subscribe(cacheKey, callback),\r\n () => globalStateManager.getSnapshot<ActionState<OutputOf<typeof actionConfig>>>(cacheKey)\r\n );\r\n\r\n useEffect(() => {\r\n if (enabled && state.isStale && !state.loading) {\r\n actions[actionName].execute(input as never);\r\n }\r\n }, [enabled, state.isStale, state.loading, input, actions, actionName]);\r\n \r\n return state;\r\n },\r\n enumerable: true,\r\n });\r\n }\r\n return finalStates as ModuleStates<TActions>;\r\n }, [moduleConfig, pathParamsString, queries, enabled, actions]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n const actionKeys = Object.keys(moduleConfig.actions) as Array<keyof TActions>;\r\n for (const actionName of actionKeys) {\r\n const state = states[actionName];\r\n const actionConfig = moduleConfig.actions[actionName];\r\n if (actionConfig.autoFetch && state && !state.called && !state.loading) {\r\n const query = queries[actionName as keyof typeof queries]?.options;\r\n const input: unknown = actionConfig.hasQuery ? query : undefined;\r\n actions[actionName].execute(input as never);\r\n }\r\n }\r\n }, [enabled, moduleConfig, actions, states, queries]);\r\n\r\n const lastBlurTimestamp = useRef(Date.now());\r\n useEffect(() => {\r\n if (!enabled || !refetchOnWindowFocus) return;\r\n const onFocus = () => {\r\n if (Date.now() - lastBlurTimestamp.current > 10000) {\r\n const actionKeys = Object.keys(moduleConfig.actions) as Array<keyof TActions>;\r\n for (const actionName of actionKeys) {\r\n const state = states[actionName];\r\n if (state && state.called && !state.loading) {\r\n const prefix = `${moduleConfig.baseEndpoint}/${actionName as string}::`;\r\n globalStateManager.invalidateByPrefix(prefix);\r\n }\r\n }\r\n }\r\n };\r\n const onBlur = () => { lastBlurTimestamp.current = Date.now(); };\r\n\r\n window.addEventListener('focus', onFocus);\r\n window.addEventListener('blur', onBlur);\r\n return () => {\r\n window.removeEventListener('focus', onFocus);\r\n window.removeEventListener('blur', onBlur);\r\n };\r\n }, [enabled, refetchOnWindowFocus, moduleConfig, states]);\r\n\r\n const dehydrate = useMemo(() => {\r\n return () => globalStateManager.dehydrate();\r\n }, []);\r\n\r\n return { actions, states, queries, dehydrate };\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useMemo, useEffect, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import { StandardResponse, QueryOptions, ApiError, UseApiQuery, ModuleActions, UseApiModuleOptions, ActionState, ApiModuleConfig, ActionConfigModule } from '../../types';\r\n// import { cacheManager } from '../../core/cache';\r\n// import { callDynamicApi } from '../../services/crud';\r\n\r\n\r\n\r\n// const generateCacheKey = (\r\n// moduleName: string,\r\n// actionName: string,\r\n// input?: any,\r\n// callOptions: { pathParams?: Record<string, any>; query?: QueryOptions } = {}\r\n// ): string => {\r\n// // تم تبسيط هذا الجزء ليكون أكثر أمانًا\r\n// const params = {\r\n// path: callOptions.pathParams,\r\n// body: input,\r\n// query: callOptions.query,\r\n// };\r\n// try {\r\n// return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n// } catch (error) {\r\n// console.error(\"Could not stringify API params for cache key:\", error);\r\n// return `${moduleName}/${actionName}::${Date.now()}`;\r\n// }\r\n// };\r\n\r\n// export function useApiModule<TModule extends ApiModuleConfig>(\r\n// axiosInstance: AxiosInstance,\r\n// moduleConfig: TModule,\r\n// options: UseApiModuleOptions = {}\r\n// ): any {\r\n// const { staleTime = 0, cacheTime, refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams } = options;\r\n\r\n// const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n\r\n// const [states, setStates] = useState<Record<keyof TModule['actions'], ActionState<any>>>(() => {\r\n// const initialStates: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// initialStates[actionName] = {\r\n// data: null, links: undefined, meta: undefined, error: null,\r\n// loading: false, success: false, called: false, message: undefined,\r\n// validationErrors: [], rawResponse: null,\r\n// };\r\n// }\r\n// return initialStates as Record<keyof TModule['actions'], ActionState<any>>;\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n// const savedCallbacks = useRef({ onSuccess, onError });\r\n// useEffect(() => {\r\n// savedCallbacks.current = { onSuccess, onError };\r\n// }, [onSuccess, onError]);\r\n\r\n// const actions = useMemo(() => {\r\n// const builtActions: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// const actionConfig = moduleConfig.actions[actionName] as ActionConfigModule<any, any>;\r\n \r\n// const execute = async (\r\n// input?: any,\r\n// callOptions: { pathParams?: any; config?: any; query?: any } = {}\r\n// ): Promise<StandardResponse<any>> => {\r\n// const updateState = (partialState: Partial<ActionState<any>>) => {\r\n// setStates(prev => ({ ...prev, [actionName]: { ...(prev[actionName] as object), ...partialState } }));\r\n// };\r\n \r\n// const finalPathParams = { ...(JSON.parse(pathParamsString)), ...callOptions.pathParams };\r\n// const finalCallOptions = { ...callOptions, pathParams: finalPathParams };\r\n\r\n// const isGetRequest = actionConfig.method === 'GET';\r\n// if (isGetRequest) {\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n \r\n// // [FIX 1] نحدد النوع المتوقع من الكاش وهو StandardResponse<any>\r\n// const cachedEntry = cacheManager.getWithMeta<StandardResponse<any>>(cacheKey);\r\n\r\n// // [FIX 2] الآن cachedEntry هو إما null أو CacheItem<StandardResponse<any>>\r\n// if (cachedEntry) {\r\n// const isStale = Date.now() - cachedEntry.timestamp > staleTime;\r\n\r\n// if (!isStale) {\r\n// // الآن cachedEntry.data هو من نوع StandardResponse<any>، وليس unknown\r\n// const cachedState: ActionState<any> = {\r\n// ...cachedEntry.data, // لا توجد مشكلة الآن\r\n// loading: false,\r\n// called: true,\r\n// };\r\n// updateState(cachedState);\r\n// return cachedEntry.data; // لا توجد مشكلة الآن\r\n// }\r\n// // البيانات قديمة، اعرضها وقم بالتحديث في الخلفية\r\n// updateState({ ...cachedEntry.data, loading: true, called: true }); // لا توجد مشكلة الآن\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n\r\n// try {\r\n// // باقي الكود يبقى كما هو لأنه صحيح\r\n// const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n// pathParams: finalPathParams,\r\n// body: input,\r\n// config: callOptions.config,\r\n// });\r\n \r\n// updateState({ ...result, loading: false });\r\n \r\n// if (result.success) {\r\n// // [FIX 3] عند تخزين البيانات في الكاش، يجب تخزين الكائن result الكامل\r\n// if (isGetRequest) {\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n// cacheManager.set(cacheKey, result, cacheTime);\r\n// }\r\n// savedCallbacks.current.onSuccess?.(actionName, result.message || 'Action successful', result.data);\r\n// } else {\r\n// savedCallbacks.current.onError?.(actionName, result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n \r\n// return result;\r\n// } catch (error: any) {\r\n// const apiError: ApiError = { status: 500, message: error.message || \"An unexpected error occurred.\" };\r\n// const errorResult: StandardResponse<any> = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n// updateState({ ...errorResult, loading: false });\r\n \r\n// savedCallbacks.current.onError?.(actionName, apiError.message, apiError);\r\n// return errorResult;\r\n// }\r\n// };\r\n// const reset = () => {\r\n// setStates(prev => ({ ...prev, [actionName]: {\r\n// data: null, links: undefined, meta: undefined, error: null,\r\n// loading: false, success: false, called: false, message: undefined,\r\n// validationErrors: [], rawResponse: null,\r\n// }}));\r\n// };\r\n\r\n// builtActions[actionName] = { execute, reset };\r\n// }\r\n// return builtActions as ModuleActions<TModule>;\r\n// }, [axiosInstance, moduleConfig, staleTime, cacheTime, pathParamsString]);\r\n\r\n // const queries = useMemo(() => {\r\n // const builtQueries: Record<string, UseApiQuery | undefined> = {};\r\n // for (const actionName in moduleConfig.actions) {\r\n // if (moduleConfig.actions[actionName]?.isList) {\r\n // const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n // setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n // };\r\n // builtQueries[actionName] = {\r\n // options: queryOptions[actionName] || {},\r\n // setOptions: setActionQueryOptions,\r\n // setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n // setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n // setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n // setFilters: (filter: Record<string, any>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n // setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n // setQueryParam: (key: string, value: any) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? 1 : value })),\r\n // reset: () => setActionQueryOptions({}),\r\n // };\r\n // }\r\n // }\r\n // return builtQueries;\r\n // }, [queryOptions, moduleConfig.actions]);\r\n\r\n// useEffect(() => {\r\n// if (!refetchOnWindowFocus) return;\r\n// const onFocus = () => {\r\n// // يمكنك هنا إضافة منطق إعادة الجلب عند التركيز على النافذة\r\n// };\r\n// window.addEventListener('focus', onFocus);\r\n// return () => window.removeEventListener('focus', onFocus);\r\n// }, [refetchOnWindowFocus, actions]);\r\n\r\n// return {\r\n// actions,\r\n// states,\r\n// queries,\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// import { useState, useMemo, useEffect, useRef } from 'react';\r\n// import { AxiosInstance } from 'axios';\r\n// import {\r\n// ApiModuleConfig,\r\n// ModuleActions,\r\n// ActionState,\r\n// ActionConfig,\r\n// UseApiModuleOptions,\r\n// } from '../../types/apiModule.types'; \r\n// import { StandardResponse, QueryOptions, ApiError } from '../../types'; \r\n// import { cacheManager } from '@/core/cache';\r\n// import { callDynamicApi } from '@/services/crud';\r\n// import { UseApiQuery } from '@/types/useApi.types';\r\n\r\n// const generateCacheKey = (\r\n// moduleName: string,\r\n// actionName: string,\r\n// input?: any,\r\n// callOptions: { pathParams?: Record<string, any>; query?: QueryOptions } = {}\r\n// ): string => {\r\n// const params = { path: callOptions.pathParams, body: input, query: callOptions.query };\r\n// try {\r\n// return `${moduleName}/${actionName}::${JSON.stringify(params)}`;\r\n// } catch (error) {\r\n// console.error(\"Could not stringify API params for cache key:\", error);\r\n// return `${moduleName}/${actionName}::${Date.now()}`;\r\n// }\r\n// };\r\n\r\n\r\n// export function useApiModule<TModule extends ApiModuleConfig>(\r\n// axiosInstance: AxiosInstance,\r\n// moduleConfig: TModule,\r\n// options: UseApiModuleOptions = {}\r\n// ):any {\r\n// const { staleTime = 0, cacheTime, refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams } = options;\r\n\r\n// const pathParamsString = useMemo(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);\r\n\r\n// const [states, setStates] = useState<Record<keyof TModule['actions'], ActionState<any>>>(() => {\r\n// const initialStates: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// initialStates[actionName] = { data: null, error: null, loading: false, success: false, called: false, response: null };\r\n// }\r\n// return initialStates as Record<keyof TModule['actions'], ActionState<any>>;\r\n// });\r\n\r\n// const [queryOptions, setQueryOptions] = useState<Record<string, QueryOptions>>({});\r\n \r\n// const savedCallbacks = useRef({ onSuccess, onError });\r\n// useEffect(() => {\r\n// savedCallbacks.current = { onSuccess, onError };\r\n// }, [onSuccess, onError]);\r\n\r\n// const actions = useMemo(() => {\r\n// const builtActions: any = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// const actionConfig = moduleConfig.actions[actionName] as ActionConfig<any, any>;\r\n \r\n// const execute = async (\r\n// input?: any,\r\n// callOptions: { pathParams?: any; config?: any; query?: any } = {}\r\n// ): Promise<StandardResponse<any>> => {\r\n// const updateState = (partialState: Partial<ActionState<any>>) => {\r\n// setStates(prev => ({ ...prev, [actionName]: { ...(prev[actionName]), ...partialState } }));\r\n// };\r\n \r\n// const finalPathParams = { ...(JSON.parse(pathParamsString)), ...callOptions.pathParams };\r\n// const finalCallOptions = { ...callOptions, pathParams: finalPathParams };\r\n\r\n// const isGetRequest = actionConfig.method === 'GET';\r\n// const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, finalCallOptions);\r\n \r\n// if (isGetRequest) {\r\n// const cachedEntry = cacheManager.getWithMeta(cacheKey);\r\n// if (cachedEntry) {\r\n// const isStale = Date.now() - cachedEntry.timestamp > staleTime;\r\n// if (!isStale) {\r\n// const result: StandardResponse<any> = { data: cachedEntry.data, success: true, loading: false, error: null, rawResponse: undefined };\r\n// updateState({ ...result, called: true, response: result });\r\n// return result;\r\n// }\r\n// updateState({ data: cachedEntry.data, loading: true, error: null, called: true });\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n// } else {\r\n// updateState({ loading: true, error: null, called: true });\r\n// }\r\n\r\n// try {\r\n// const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {\r\n// pathParams: finalPathParams,\r\n// body: input,\r\n// config: callOptions.config,\r\n// });\r\n \r\n// if (result.success) {\r\n// savedCallbacks.current.onSuccess?.(actionName, result.message || 'Action successful', result.data);\r\n// } else {\r\n// savedCallbacks.current.onError?.(actionName, result.message || 'Action failed', result.error ?? undefined);\r\n// }\r\n \r\n// updateState({ loading: false, data: result.data, error: result.error, success: result.success, response: result });\r\n// return result;\r\n// } catch (error: any) {\r\n// const apiError: ApiError = { status: 500, message: error.message || \"An unexpected error occurred.\" };\r\n// const errorResult: StandardResponse<any> = { data: null, error: apiError, loading: false, success: false, rawResponse: error };\r\n// updateState({ ...errorResult, response: errorResult });\r\n \r\n// savedCallbacks.current.onError?.(actionName, apiError.message, apiError);\r\n// return errorResult;\r\n// }\r\n// };\r\n// const reset = () => {\r\n// setStates(prev => ({ ...prev, [actionName]: { data: null, error: null, loading: false, success: false, called: false, response: null } }));\r\n// };\r\n\r\n// builtActions[actionName] = { execute, reset };\r\n// }\r\n// return builtActions as ModuleActions<TModule>;\r\n// }, [axiosInstance, moduleConfig, staleTime, cacheTime, pathParamsString]);\r\n\r\n// const queries = useMemo(() => {\r\n// const builtQueries: Record<string, UseApiQuery | undefined> = {};\r\n// for (const actionName in moduleConfig.actions) {\r\n// if (moduleConfig.actions[actionName]?.isList) {\r\n// const setActionQueryOptions = (updater: React.SetStateAction<QueryOptions>) => {\r\n// setQueryOptions(prev => ({ ...prev, [actionName]: typeof updater === 'function' ? updater(prev[actionName] || {}) : updater }));\r\n// };\r\n// builtQueries[actionName] = {\r\n// options: queryOptions[actionName] || {},\r\n// setOptions: setActionQueryOptions,\r\n// setPage: (page: number) => setActionQueryOptions(p => ({ ...p, page })),\r\n// setLimit: (limit: number) => setActionQueryOptions(p => ({ ...p, limit, page: 1 })),\r\n// setSearchTerm: (search: string) => setActionQueryOptions(p => ({ ...p, search, page: 1 })),\r\n// setFilters: (filter: Record<string, any>) => setActionQueryOptions(p => ({ ...p, filter, page: 1 })),\r\n// setSorting: (sortBy: { key: string; direction: 'asc' | 'desc' }[]) => setActionQueryOptions(p => ({ ...p, sortBy })),\r\n// setQueryParam: (key: string, value: any) => setActionQueryOptions(p => ({ ...p, [key]: value, page: key !== 'page' ? 1 : value })),\r\n// reset: () => setActionQueryOptions({}),\r\n// };\r\n// }\r\n// }\r\n// return builtQueries;\r\n// }, [queryOptions, moduleConfig.actions]);\r\n\r\n\r\n\r\n\r\n// useEffect(() => {\r\n// if (!refetchOnWindowFocus) return;\r\n// const onFocus = () => {\r\n// console.log(\"Window focused. Consider implementing refetch logic.\");\r\n// };\r\n// window.addEventListener('focus', onFocus);\r\n// return () => window.removeEventListener('focus', onFocus);\r\n// }, [refetchOnWindowFocus, actions]);\r\n\r\n\r\n// return {\r\n// actions,\r\n// states,\r\n// queries,\r\n// };\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"],"mappings":"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","names":["axios","uuidv4","import_axios","import_axios","axios","axios","import_react","buildDynamicPath","import_react","isEqual","import_react","options"]}
|