api-core-lib 12.0.89 → 12.0.91

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/cli.cjs CHANGED
@@ -447,10 +447,11 @@ import type { ${schemasToImport.join(", ")} } from './types';
447
447
  }
448
448
  const indexFilePath = import_path.default.join(moduleOutputPath, "index.ts");
449
449
  const initialIndexContent = `// This file is auto-generated. Do not edit directly.
450
+ // This file is the main entry point for client-side code.
450
451
 
451
452
  ` + createdFileExports.map((e) => `export * from './${e}';`).join("\n");
452
453
  import_fs.default.writeFileSync(indexFilePath, initialIndexContent);
453
- console.log(import_chalk.default.gray(` \u2713 index.ts (Initial)`));
454
+ console.log(import_chalk.default.gray(` \u2713 index.ts (Initial Client Entry Point)`));
454
455
  const moduleBaseName = module2.moduleName.replace(/Api$/, "");
455
456
  const camelCaseModuleName = toCamelCase(moduleBaseName);
456
457
  let endpointsContent = `// This file is auto-generated. Do not edit directly.
@@ -513,7 +514,6 @@ import { createApiModuleContext, useApiModule, UseApiModuleOptions } from 'api-c
513
514
  import { apiClient } from '@/lib/api-core/clientApi'; // Assuming a fixed path
514
515
  import { ${module2.moduleName}, ${module2.moduleName} as TModuleType } from './config';
515
516
 
516
- // 1. Create the strongly-typed context, provider, and consumer hook
517
517
  const {
518
518
  Provider,
519
519
  useContext: use${moduleBaseName}Context
@@ -521,7 +521,6 @@ const {
521
521
 
522
522
  export { use${moduleBaseName}Context };
523
523
 
524
- // 2. Create a custom Provider that encapsulates the useApiModule logic
525
524
  type Options = Parameters<typeof useApiModule>[2];
526
525
 
527
526
  interface ${moduleBaseName}ProviderProps {
@@ -540,7 +539,7 @@ export function ${moduleBaseName}Provider({ children, options = {} }: ${moduleBa
540
539
  const contextFileName = `${camelCaseModuleName}.context.tsx`;
541
540
  import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, contextFileName), contextFileContent.trim());
542
541
  console.log(import_chalk.default.gray(` \u2713 ${contextFileName} (Strongly-Typed Context & Provider)`));
543
- const serverContent = `// This file is auto-generated. For server-side use only.
542
+ const serverHelperContent = `// This file is auto-generated. For server-side use only.
544
543
 
545
544
  import { createServerApi } from 'api-core-lib/server';
546
545
  import { serverApiClient } from '@/lib/api-core/serverApi'; // Assuming a fixed path
@@ -553,17 +552,25 @@ export const create${moduleBaseName}ServerApi = () => {
553
552
  return createServerApi(serverApiClient, ${module2.moduleName});
554
553
  };
555
554
  `;
556
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.server.ts`), serverContent.trim());
555
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.server.ts`), serverHelperContent.trim());
557
556
  console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.server.ts (Server-Side Helper)`));
558
- const newExports = [
557
+ const clientExports = [
559
558
  `
560
- // Generated API helpers`,
559
+ // Generated API client helpers`,
561
560
  `export * from './${camelCaseModuleName}.endpoints';`,
562
- `export * from './${camelCaseModuleName}.context';`,
563
- `export * from './${camelCaseModuleName}.server';`
561
+ `export * from './${camelCaseModuleName}.context';`
564
562
  ].join("\n");
565
- import_fs.default.appendFileSync(indexFilePath, newExports);
566
- console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with helpers)`));
563
+ import_fs.default.appendFileSync(indexFilePath, clientExports);
564
+ console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with client helpers)`));
565
+ const serverEntryPointContent = `// This file is auto-generated. For server-side use only.
566
+ // It serves as the entry point for server-specific utilities.
567
+
568
+ export * from './${camelCaseModuleName}.server';
569
+ // Also exporting endpoints, as they are environment-agnostic and useful on the server.
570
+ export * from './${camelCaseModuleName}.endpoints';
571
+ `;
572
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "server.ts"), serverEntryPointContent);
573
+ console.log(import_chalk.default.gray(` \u2713 server.ts (New Server Entry Point)`));
567
574
  }
568
575
  function _propToMock(prop) {
569
576
  if (prop.example) return prop.example;
package/dist/client.cjs CHANGED
@@ -270,15 +270,19 @@ function useDeepCompareEffect(callback, dependencies) {
270
270
  _react.useEffect.call(void 0, callback, [currentDependenciesRef.current]);
271
271
  }
272
272
 
273
- // src/hooks/useApiModule/useApiModule.ts
273
+ // src/hooks/useApiModule/useApiModule.v1.ts
274
274
 
275
275
  var ApiModuleContext = _react.createContext.call(void 0, null);
276
276
  var ApiModuleProvider = ApiModuleContext.Provider;
277
277
  var createInitialState = () => ({
278
278
  data: null,
279
279
  lastSuccessAt: void 0,
280
- meta: [],
281
- validationErrors: [],
280
+ meta: void 0,
281
+ // meta هو كائن، وليس مصفوفة
282
+ links: void 0,
283
+ // links هو كائن، وليس مصفوفة
284
+ validationErrors: void 0,
285
+ // validationErrors هو مصفوفة، ولكن يمكن أن يكون غير موجود
282
286
  error: null,
283
287
  loading: false,
284
288
  success: false,
@@ -287,34 +291,29 @@ var createInitialState = () => ({
287
291
  rawResponse: null
288
292
  });
289
293
  function useApiActionState(actionConfig, cacheKey, execute, input, enabled) {
290
- const getClientSnapshot = () => _chunk25UFVV4Fcjs.globalStateManager.getSnapshot(cacheKey);
291
- const getServerSnapshot = () => _chunk25UFVV4Fcjs.globalStateManager.getSnapshot(cacheKey);
294
+ const getClientSnapshot = _react.useCallback.call(void 0, () => _chunk25UFVV4Fcjs.globalStateManager.getSnapshot(cacheKey), [cacheKey]);
295
+ const getServerSnapshot = _react.useCallback.call(void 0, () => _chunk25UFVV4Fcjs.globalStateManager.getSnapshot(cacheKey), [cacheKey]);
292
296
  const state = _react.useSyncExternalStore.call(void 0,
293
297
  (callback) => _chunk25UFVV4Fcjs.globalStateManager.subscribe(cacheKey, callback),
294
298
  getClientSnapshot,
295
299
  getServerSnapshot
296
300
  );
297
- const inputRef = _react.useRef.call(void 0, input);
298
- _react.useEffect.call(void 0, () => {
299
- inputRef.current = input;
300
- }, [input]);
301
301
  const refetch = _react.useCallback.call(void 0, () => {
302
- execute(inputRef.current);
303
- }, [execute]);
304
- const prevCacheKeyRef = _react.useRef.call(void 0, cacheKey);
302
+ execute(input);
303
+ }, [execute, input]);
304
+ const prevStateRef = _react.useRef.call(void 0, );
305
305
  _react.useEffect.call(void 0, () => {
306
- if (prevCacheKeyRef.current !== cacheKey && enabled && state.called) {
307
- console.log(`[Cache Key Changed] from ${prevCacheKeyRef.current} to ${cacheKey}. Refetching...`);
306
+ const currentState = state || createInitialState();
307
+ const previousState = prevStateRef.current;
308
+ if (enabled && actionConfig.autoFetch && !currentState.called && !currentState.loading) {
308
309
  refetch();
309
- } else if (enabled && actionConfig.autoFetch && !state.called && !state.loading) {
310
- console.log(`[Auto Fetch] for ${cacheKey}. Fetching...`);
310
+ } else if (enabled && currentState.isStale && !currentState.loading) {
311
311
  refetch();
312
- } else if (enabled && state.isStale && !state.loading) {
313
- console.log(`[Stale State] for ${cacheKey}. Refetching...`);
312
+ } else if (enabled && _optionalChain([previousState, 'optionalAccess', _6 => _6.called]) && !currentState.loading && cacheKey !== _optionalChain([prevStateRef, 'access', _7 => _7.current, 'optionalAccess', _8 => _8.cacheKey])) {
314
313
  refetch();
315
314
  }
316
- prevCacheKeyRef.current = cacheKey;
317
- }, [cacheKey, enabled, state.isStale, state.loading, state.called, actionConfig.autoFetch, refetch]);
315
+ prevStateRef.current = { ...currentState, cacheKey };
316
+ }, [cacheKey, enabled, state, actionConfig.autoFetch, refetch]);
318
317
  return state;
319
318
  }
320
319
  function useModuleContext() {
@@ -340,15 +339,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
340
339
  _react.useEffect.call(void 0, () => {
341
340
  savedCallbacks.current = { onSuccess, onError };
342
341
  }, [onSuccess, onError]);
343
- _react.useMemo.call(void 0, () => {
344
- if (hydratedState) {
345
- _chunk25UFVV4Fcjs.globalStateManager.rehydrate(hydratedState);
346
- }
347
- }, [hydratedState]);
348
342
  _react.useEffect.call(void 0, () => {
349
- savedCallbacks.current = { onSuccess, onError };
350
- }, [onSuccess, onError]);
351
- _react.useMemo.call(void 0, () => {
352
343
  if (hydratedState) {
353
344
  _chunk25UFVV4Fcjs.globalStateManager.rehydrate(hydratedState);
354
345
  }
@@ -358,7 +349,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
358
349
  const actionConfig = moduleConfig.actions[actionName];
359
350
  const shouldCache = actionConfig.cacheResponse !== false;
360
351
  const execute = async (input, options2 = {}) => {
361
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
352
+ const finalPathParams = { ...modulePathParams || {}, ...options2.pathParams };
362
353
  const cacheKey = shouldCache ? _chunk25UFVV4Fcjs.generateCacheKey.call(void 0, moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams }) : "";
363
354
  let mutationContext;
364
355
  try {
@@ -373,48 +364,41 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
373
364
  body: input,
374
365
  config: options2.config
375
366
  });
376
- console.log("[API Result execute]", result);
377
367
  if (shouldCache) {
378
368
  _chunk25UFVV4Fcjs.globalStateManager.setState(cacheKey, (prev) => ({
379
369
  ...prev,
380
- // احتفظ بالخصائص القديمة مثل 'called'
381
370
  loading: false,
382
371
  success: result.success,
383
- // تحديث صريح
384
372
  error: result.success ? null : result.error || prev.error,
385
- // تحديث صريح
386
- data: result.data,
387
- // تحديث صريح للبيانات
373
+ data: result.success ? result.data : prev.data,
374
+ // ✅ [تحسين] لا تمسح البيانات القديمة عند حدوث خطأ
388
375
  meta: result.meta,
389
- // تحديث صريح
390
376
  links: result.links,
391
- // تحديث صريح
392
377
  message: result.message,
393
- validationErrors: result.validationErrors || [],
394
- rawResponse: result.rawResponse
395
- // isStale تم تعيينه إلى false في بداية execute، وسيظل كذلك
378
+ validationErrors: result.validationErrors,
379
+ rawResponse: result.rawResponse,
380
+ lastSuccessAt: result.success ? Date.now() : prev.lastSuccessAt
396
381
  }));
397
382
  }
398
383
  if (result.success) {
399
- _optionalChain([savedCallbacks, 'access', _6 => _6.current, 'access', _7 => _7.onSuccess, 'optionalCall', _8 => _8(actionName, result.message || "Action successful", result.data)]);
400
- _optionalChain([options2, 'access', _9 => _9.onSuccess, 'optionalCall', _10 => _10(result.data, mutationContext)]);
401
- _optionalChain([actionConfig, 'access', _11 => _11.invalidates, 'optionalAccess', _12 => _12.forEach, 'call', _13 => _13((keyToInvalidate) => {
384
+ _optionalChain([savedCallbacks, 'access', _9 => _9.current, 'access', _10 => _10.onSuccess, 'optionalCall', _11 => _11(actionName, result.message || "Action successful", result.data)]);
385
+ _optionalChain([options2, 'access', _12 => _12.onSuccess, 'optionalCall', _13 => _13(result.data, mutationContext)]);
386
+ _optionalChain([actionConfig, 'access', _14 => _14.invalidates, 'optionalAccess', _15 => _15.forEach, 'call', _16 => _16((keyToInvalidate) => {
402
387
  const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;
403
- console.log(`[Invalidating] by prefix: ${prefix}`);
404
388
  _chunk25UFVV4Fcjs.globalStateManager.invalidateByPrefix(prefix);
405
389
  })]);
406
390
  } else {
407
- _optionalChain([savedCallbacks, 'access', _14 => _14.current, 'access', _15 => _15.onError, 'optionalCall', _16 => _16(actionName, result.message || "Action failed", _nullishCoalesce(result.error, () => ( void 0)))]);
408
- _optionalChain([options2, 'access', _17 => _17.onError, 'optionalCall', _18 => _18(result.error, mutationContext)]);
391
+ _optionalChain([savedCallbacks, 'access', _17 => _17.current, 'access', _18 => _18.onError, 'optionalCall', _19 => _19(actionName, result.message || "Action failed", _nullishCoalesce(result.error, () => ( void 0)))]);
392
+ _optionalChain([options2, 'access', _20 => _20.onError, 'optionalCall', _21 => _21(result.error, mutationContext)]);
409
393
  }
410
394
  return result;
411
395
  } catch (error) {
412
- const apiError = _optionalChain([error, 'access', _19 => _19.response, 'optionalAccess', _20 => _20.data]) || { status: 500, message: error.message };
396
+ const apiError = _optionalChain([error, 'access', _22 => _22.response, 'optionalAccess', _23 => _23.data]) || { status: 500, message: error.message };
413
397
  if (shouldCache) {
414
398
  _chunk25UFVV4Fcjs.globalStateManager.setState(cacheKey, (prev) => ({ ...prev, error: apiError, loading: false, success: false }));
415
399
  }
416
- _optionalChain([savedCallbacks, 'access', _21 => _21.current, 'access', _22 => _22.onError, 'optionalCall', _23 => _23(actionName, apiError.message, apiError)]);
417
- _optionalChain([options2, 'access', _24 => _24.onError, 'optionalCall', _25 => _25(apiError, mutationContext)]);
400
+ _optionalChain([savedCallbacks, 'access', _24 => _24.current, 'access', _25 => _25.onError, 'optionalCall', _26 => _26(actionName, apiError.message, apiError)]);
401
+ _optionalChain([options2, 'access', _27 => _27.onError, 'optionalCall', _28 => _28(apiError, mutationContext)]);
418
402
  throw error;
419
403
  } finally {
420
404
  if (options2.onSettled) {
@@ -424,7 +408,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
424
408
  };
425
409
  const reset = (input, options2 = {}) => {
426
410
  if (shouldCache) {
427
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
411
+ const finalPathParams = { ...modulePathParams || {}, ...options2.pathParams };
428
412
  const cacheKey = _chunk25UFVV4Fcjs.generateCacheKey.call(void 0, moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams });
429
413
  _chunk25UFVV4Fcjs.globalStateManager.setState(cacheKey, () => createInitialState());
430
414
  }
@@ -432,11 +416,11 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
432
416
  acc[actionName] = { execute, reset };
433
417
  return acc;
434
418
  }, {});
435
- }, [axiosInstance, moduleConfig, pathParamsString]);
419
+ }, [axiosInstance, moduleConfig, modulePathParams]);
436
420
  const queries = _react.useMemo.call(void 0, () => {
437
421
  const builtQueries = {};
438
422
  for (const actionName in moduleConfig.actions) {
439
- if (_optionalChain([moduleConfig, 'access', _26 => _26.actions, 'access', _27 => _27[actionName], 'optionalAccess', _28 => _28.hasQuery])) {
423
+ if (_optionalChain([moduleConfig, 'access', _29 => _29.actions, 'access', _30 => _30[actionName], 'optionalAccess', _31 => _31.hasQuery])) {
440
424
  const setActionQueryOptions = (updater) => {
441
425
  setQueryOptions((prev) => ({ ...prev, [actionName]: typeof updater === "function" ? updater(prev[actionName] || {}) : updater }));
442
426
  };
@@ -448,50 +432,42 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
448
432
  setSearchTerm: (search) => setActionQueryOptions((p) => ({ ...p, search, page: 1 })),
449
433
  setFilters: (filter) => setActionQueryOptions((p) => ({ ...p, filter, page: 1 })),
450
434
  setSorting: (sortBy) => setActionQueryOptions((p) => ({ ...p, sortBy })),
451
- setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? value : p.page })),
435
+ setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? 1 : value })),
452
436
  reset: () => setActionQueryOptions({})
453
437
  };
454
438
  }
455
439
  }
456
440
  return builtQueries;
457
441
  }, [queryOptions, moduleConfig.actions]);
458
- const states = {};
459
- function isActionWithQuery(key, actions2) {
460
- return _optionalChain([actions2, 'access', _29 => _29[key], 'optionalAccess', _30 => _30.hasQuery]) === true;
461
- }
462
- for (const actionName in moduleConfig.actions) {
463
- if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
464
- const actionConfig = moduleConfig.actions[actionName];
465
- if (actionConfig.cacheResponse !== false) {
466
- let queryOptions2;
467
- if (isActionWithQuery(actionName, moduleConfig.actions)) {
468
- queryOptions2 = _optionalChain([queries, 'access', _31 => _31[actionName], 'optionalAccess', _32 => _32.options]);
442
+ const states = _react.useMemo.call(void 0, () => {
443
+ const builtStates = {};
444
+ for (const actionName in moduleConfig.actions) {
445
+ if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
446
+ const actionConfig = moduleConfig.actions[actionName];
447
+ if (actionConfig.cacheResponse !== false) {
448
+ const query = _optionalChain([queries, 'access', _32 => _32[actionName], 'optionalAccess', _33 => _33.options]);
449
+ const input = actionConfig.hasQuery ? query : void 0;
450
+ const cacheKey = _chunk25UFVV4Fcjs.generateCacheKey.call(void 0, moduleConfig.baseEndpoint, actionName, input, { pathParams: modulePathParams });
451
+ builtStates[actionName] = useApiActionState(
452
+ actionConfig,
453
+ cacheKey,
454
+ actions[actionName].execute,
455
+ input,
456
+ enabled
457
+ );
458
+ } else {
459
+ builtStates[actionName] = createInitialState();
469
460
  }
470
- const input = queryOptions2;
471
- const pathParams = JSON.parse(pathParamsString);
472
- const cacheKey = _chunk25UFVV4Fcjs.generateCacheKey.call(void 0,
473
- moduleConfig.baseEndpoint,
474
- actionName,
475
- input,
476
- { pathParams }
477
- );
478
- states[actionName] = useApiActionState(
479
- actionConfig,
480
- cacheKey,
481
- actions[actionName].execute,
482
- input,
483
- enabled
484
- );
485
- } else {
486
- states[actionName] = createInitialState();
487
461
  }
488
462
  }
489
- }
463
+ return builtStates;
464
+ }, [moduleConfig, queries, actions, modulePathParams, enabled]);
490
465
  const lastBlurTimestamp = _react.useRef.call(void 0, Date.now());
491
466
  _react.useEffect.call(void 0, () => {
492
467
  if (!enabled || !refetchOnWindowFocus) return;
493
468
  const onFocus = () => {
494
- if (Date.now() - lastBlurTimestamp.current > 1e4) {
469
+ if (Date.now() - lastBlurTimestamp.current > 3e4) {
470
+ console.log("[Refetch on Focus] Invalidating all called queries for this module.");
495
471
  const actionKeys = Object.keys(moduleConfig.actions);
496
472
  for (const actionName of actionKeys) {
497
473
  const state = states[actionName];
@@ -512,15 +488,16 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
512
488
  window.removeEventListener("blur", onBlur);
513
489
  };
514
490
  }, [enabled, refetchOnWindowFocus, moduleConfig, states]);
515
- const dehydrate = _react.useMemo.call(void 0, () => {
516
- return () => _chunk25UFVV4Fcjs.globalStateManager.dehydrate();
491
+ const dehydrate = _react.useCallback.call(void 0, () => {
492
+ return _chunk25UFVV4Fcjs.globalStateManager.dehydrate();
517
493
  }, []);
518
- const baseApiReturn = { actions, states, queries, dehydrate };
519
- const finalApiReturn = _react.useMemo.call(void 0, () => ({
520
- ...baseApiReturn,
494
+ return _react.useMemo.call(void 0, () => ({
495
+ actions,
496
+ states,
497
+ queries,
498
+ dehydrate,
521
499
  ...extraContextData || {}
522
- }), [baseApiReturn, extraContextData]);
523
- return finalApiReturn;
500
+ }), [actions, states, queries, dehydrate, extraContextData]);
524
501
  }
525
502
 
526
503
  // src/hooks/useApiModule/apiModuleContext.ts
package/dist/client.js CHANGED
@@ -270,15 +270,19 @@ function useDeepCompareEffect(callback, dependencies) {
270
270
  useEffect3(callback, [currentDependenciesRef.current]);
271
271
  }
272
272
 
273
- // src/hooks/useApiModule/useApiModule.ts
273
+ // src/hooks/useApiModule/useApiModule.v1.ts
274
274
  import { createContext, useCallback as useCallback3, useContext, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3, useSyncExternalStore } from "react";
275
275
  var ApiModuleContext = createContext(null);
276
276
  var ApiModuleProvider = ApiModuleContext.Provider;
277
277
  var createInitialState = () => ({
278
278
  data: null,
279
279
  lastSuccessAt: void 0,
280
- meta: [],
281
- validationErrors: [],
280
+ meta: void 0,
281
+ // meta هو كائن، وليس مصفوفة
282
+ links: void 0,
283
+ // links هو كائن، وليس مصفوفة
284
+ validationErrors: void 0,
285
+ // validationErrors هو مصفوفة، ولكن يمكن أن يكون غير موجود
282
286
  error: null,
283
287
  loading: false,
284
288
  success: false,
@@ -287,34 +291,29 @@ var createInitialState = () => ({
287
291
  rawResponse: null
288
292
  });
289
293
  function useApiActionState(actionConfig, cacheKey, execute, input, enabled) {
290
- const getClientSnapshot = () => globalStateManager.getSnapshot(cacheKey);
291
- const getServerSnapshot = () => globalStateManager.getSnapshot(cacheKey);
294
+ const getClientSnapshot = useCallback3(() => globalStateManager.getSnapshot(cacheKey), [cacheKey]);
295
+ const getServerSnapshot = useCallback3(() => globalStateManager.getSnapshot(cacheKey), [cacheKey]);
292
296
  const state = useSyncExternalStore(
293
297
  (callback) => globalStateManager.subscribe(cacheKey, callback),
294
298
  getClientSnapshot,
295
299
  getServerSnapshot
296
300
  );
297
- const inputRef = useRef4(input);
298
- useEffect4(() => {
299
- inputRef.current = input;
300
- }, [input]);
301
301
  const refetch = useCallback3(() => {
302
- execute(inputRef.current);
303
- }, [execute]);
304
- const prevCacheKeyRef = useRef4(cacheKey);
302
+ execute(input);
303
+ }, [execute, input]);
304
+ const prevStateRef = useRef4();
305
305
  useEffect4(() => {
306
- if (prevCacheKeyRef.current !== cacheKey && enabled && state.called) {
307
- console.log(`[Cache Key Changed] from ${prevCacheKeyRef.current} to ${cacheKey}. Refetching...`);
306
+ const currentState = state || createInitialState();
307
+ const previousState = prevStateRef.current;
308
+ if (enabled && actionConfig.autoFetch && !currentState.called && !currentState.loading) {
308
309
  refetch();
309
- } else if (enabled && actionConfig.autoFetch && !state.called && !state.loading) {
310
- console.log(`[Auto Fetch] for ${cacheKey}. Fetching...`);
310
+ } else if (enabled && currentState.isStale && !currentState.loading) {
311
311
  refetch();
312
- } else if (enabled && state.isStale && !state.loading) {
313
- console.log(`[Stale State] for ${cacheKey}. Refetching...`);
312
+ } else if (enabled && previousState?.called && !currentState.loading && cacheKey !== prevStateRef.current?.cacheKey) {
314
313
  refetch();
315
314
  }
316
- prevCacheKeyRef.current = cacheKey;
317
- }, [cacheKey, enabled, state.isStale, state.loading, state.called, actionConfig.autoFetch, refetch]);
315
+ prevStateRef.current = { ...currentState, cacheKey };
316
+ }, [cacheKey, enabled, state, actionConfig.autoFetch, refetch]);
318
317
  return state;
319
318
  }
320
319
  function useModuleContext() {
@@ -340,15 +339,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
340
339
  useEffect4(() => {
341
340
  savedCallbacks.current = { onSuccess, onError };
342
341
  }, [onSuccess, onError]);
343
- useMemo3(() => {
344
- if (hydratedState) {
345
- globalStateManager.rehydrate(hydratedState);
346
- }
347
- }, [hydratedState]);
348
342
  useEffect4(() => {
349
- savedCallbacks.current = { onSuccess, onError };
350
- }, [onSuccess, onError]);
351
- useMemo3(() => {
352
343
  if (hydratedState) {
353
344
  globalStateManager.rehydrate(hydratedState);
354
345
  }
@@ -358,7 +349,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
358
349
  const actionConfig = moduleConfig.actions[actionName];
359
350
  const shouldCache = actionConfig.cacheResponse !== false;
360
351
  const execute = async (input, options2 = {}) => {
361
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
352
+ const finalPathParams = { ...modulePathParams || {}, ...options2.pathParams };
362
353
  const cacheKey = shouldCache ? generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams }) : "";
363
354
  let mutationContext;
364
355
  try {
@@ -373,26 +364,20 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
373
364
  body: input,
374
365
  config: options2.config
375
366
  });
376
- console.log("[API Result execute]", result);
377
367
  if (shouldCache) {
378
368
  globalStateManager.setState(cacheKey, (prev) => ({
379
369
  ...prev,
380
- // احتفظ بالخصائص القديمة مثل 'called'
381
370
  loading: false,
382
371
  success: result.success,
383
- // تحديث صريح
384
372
  error: result.success ? null : result.error || prev.error,
385
- // تحديث صريح
386
- data: result.data,
387
- // تحديث صريح للبيانات
373
+ data: result.success ? result.data : prev.data,
374
+ // ✅ [تحسين] لا تمسح البيانات القديمة عند حدوث خطأ
388
375
  meta: result.meta,
389
- // تحديث صريح
390
376
  links: result.links,
391
- // تحديث صريح
392
377
  message: result.message,
393
- validationErrors: result.validationErrors || [],
394
- rawResponse: result.rawResponse
395
- // isStale تم تعيينه إلى false في بداية execute، وسيظل كذلك
378
+ validationErrors: result.validationErrors,
379
+ rawResponse: result.rawResponse,
380
+ lastSuccessAt: result.success ? Date.now() : prev.lastSuccessAt
396
381
  }));
397
382
  }
398
383
  if (result.success) {
@@ -400,7 +385,6 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
400
385
  options2.onSuccess?.(result.data, mutationContext);
401
386
  actionConfig.invalidates?.forEach((keyToInvalidate) => {
402
387
  const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;
403
- console.log(`[Invalidating] by prefix: ${prefix}`);
404
388
  globalStateManager.invalidateByPrefix(prefix);
405
389
  });
406
390
  } else {
@@ -424,7 +408,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
424
408
  };
425
409
  const reset = (input, options2 = {}) => {
426
410
  if (shouldCache) {
427
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
411
+ const finalPathParams = { ...modulePathParams || {}, ...options2.pathParams };
428
412
  const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams });
429
413
  globalStateManager.setState(cacheKey, () => createInitialState());
430
414
  }
@@ -432,7 +416,7 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
432
416
  acc[actionName] = { execute, reset };
433
417
  return acc;
434
418
  }, {});
435
- }, [axiosInstance, moduleConfig, pathParamsString]);
419
+ }, [axiosInstance, moduleConfig, modulePathParams]);
436
420
  const queries = useMemo3(() => {
437
421
  const builtQueries = {};
438
422
  for (const actionName in moduleConfig.actions) {
@@ -448,50 +432,42 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
448
432
  setSearchTerm: (search) => setActionQueryOptions((p) => ({ ...p, search, page: 1 })),
449
433
  setFilters: (filter) => setActionQueryOptions((p) => ({ ...p, filter, page: 1 })),
450
434
  setSorting: (sortBy) => setActionQueryOptions((p) => ({ ...p, sortBy })),
451
- setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? value : p.page })),
435
+ setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? 1 : value })),
452
436
  reset: () => setActionQueryOptions({})
453
437
  };
454
438
  }
455
439
  }
456
440
  return builtQueries;
457
441
  }, [queryOptions, moduleConfig.actions]);
458
- const states = {};
459
- function isActionWithQuery(key, actions2) {
460
- return actions2[key]?.hasQuery === true;
461
- }
462
- for (const actionName in moduleConfig.actions) {
463
- if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
464
- const actionConfig = moduleConfig.actions[actionName];
465
- if (actionConfig.cacheResponse !== false) {
466
- let queryOptions2;
467
- if (isActionWithQuery(actionName, moduleConfig.actions)) {
468
- queryOptions2 = queries[actionName]?.options;
442
+ const states = useMemo3(() => {
443
+ const builtStates = {};
444
+ for (const actionName in moduleConfig.actions) {
445
+ if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
446
+ const actionConfig = moduleConfig.actions[actionName];
447
+ if (actionConfig.cacheResponse !== false) {
448
+ const query = queries[actionName]?.options;
449
+ const input = actionConfig.hasQuery ? query : void 0;
450
+ const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: modulePathParams });
451
+ builtStates[actionName] = useApiActionState(
452
+ actionConfig,
453
+ cacheKey,
454
+ actions[actionName].execute,
455
+ input,
456
+ enabled
457
+ );
458
+ } else {
459
+ builtStates[actionName] = createInitialState();
469
460
  }
470
- const input = queryOptions2;
471
- const pathParams = JSON.parse(pathParamsString);
472
- const cacheKey = generateCacheKey(
473
- moduleConfig.baseEndpoint,
474
- actionName,
475
- input,
476
- { pathParams }
477
- );
478
- states[actionName] = useApiActionState(
479
- actionConfig,
480
- cacheKey,
481
- actions[actionName].execute,
482
- input,
483
- enabled
484
- );
485
- } else {
486
- states[actionName] = createInitialState();
487
461
  }
488
462
  }
489
- }
463
+ return builtStates;
464
+ }, [moduleConfig, queries, actions, modulePathParams, enabled]);
490
465
  const lastBlurTimestamp = useRef4(Date.now());
491
466
  useEffect4(() => {
492
467
  if (!enabled || !refetchOnWindowFocus) return;
493
468
  const onFocus = () => {
494
- if (Date.now() - lastBlurTimestamp.current > 1e4) {
469
+ if (Date.now() - lastBlurTimestamp.current > 3e4) {
470
+ console.log("[Refetch on Focus] Invalidating all called queries for this module.");
495
471
  const actionKeys = Object.keys(moduleConfig.actions);
496
472
  for (const actionName of actionKeys) {
497
473
  const state = states[actionName];
@@ -512,15 +488,16 @@ function useApiModule(axiosInstance, moduleConfig, options = {}) {
512
488
  window.removeEventListener("blur", onBlur);
513
489
  };
514
490
  }, [enabled, refetchOnWindowFocus, moduleConfig, states]);
515
- const dehydrate = useMemo3(() => {
516
- return () => globalStateManager.dehydrate();
491
+ const dehydrate = useCallback3(() => {
492
+ return globalStateManager.dehydrate();
517
493
  }, []);
518
- const baseApiReturn = { actions, states, queries, dehydrate };
519
- const finalApiReturn = useMemo3(() => ({
520
- ...baseApiReturn,
494
+ return useMemo3(() => ({
495
+ actions,
496
+ states,
497
+ queries,
498
+ dehydrate,
521
499
  ...extraContextData || {}
522
- }), [baseApiReturn, extraContextData]);
523
- return finalApiReturn;
500
+ }), [actions, states, queries, dehydrate, extraContextData]);
524
501
  }
525
502
 
526
503
  // src/hooks/useApiModule/apiModuleContext.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.89",
3
+ "version": "12.0.91",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {