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 +18 -11
- package/dist/client.cjs +67 -90
- package/dist/client.js +58 -81
- package/package.json +1 -1
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
|
|
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`),
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(
|
|
303
|
-
}, [execute]);
|
|
304
|
-
const
|
|
302
|
+
execute(input);
|
|
303
|
+
}, [execute, input]);
|
|
304
|
+
const prevStateRef = _react.useRef.call(void 0, );
|
|
305
305
|
_react.useEffect.call(void 0, () => {
|
|
306
|
-
|
|
307
|
-
|
|
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 &&
|
|
310
|
-
console.log(`[Auto Fetch] for ${cacheKey}. Fetching...`);
|
|
310
|
+
} else if (enabled && currentState.isStale && !currentState.loading) {
|
|
311
311
|
refetch();
|
|
312
|
-
} else if (enabled &&
|
|
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
|
-
|
|
317
|
-
}, [cacheKey, enabled, state
|
|
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 = { ...
|
|
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
|
-
|
|
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
|
-
|
|
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',
|
|
400
|
-
_optionalChain([options2, 'access',
|
|
401
|
-
_optionalChain([actionConfig, 'access',
|
|
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',
|
|
408
|
-
_optionalChain([options2, 'access',
|
|
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',
|
|
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',
|
|
417
|
-
_optionalChain([options2, 'access',
|
|
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 = { ...
|
|
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,
|
|
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',
|
|
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" ?
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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 >
|
|
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.
|
|
516
|
-
return
|
|
491
|
+
const dehydrate = _react.useCallback.call(void 0, () => {
|
|
492
|
+
return _chunk25UFVV4Fcjs.globalStateManager.dehydrate();
|
|
517
493
|
}, []);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
494
|
+
return _react.useMemo.call(void 0, () => ({
|
|
495
|
+
actions,
|
|
496
|
+
states,
|
|
497
|
+
queries,
|
|
498
|
+
dehydrate,
|
|
521
499
|
...extraContextData || {}
|
|
522
|
-
}), [
|
|
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
|
-
|
|
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(
|
|
303
|
-
}, [execute]);
|
|
304
|
-
const
|
|
302
|
+
execute(input);
|
|
303
|
+
}, [execute, input]);
|
|
304
|
+
const prevStateRef = useRef4();
|
|
305
305
|
useEffect4(() => {
|
|
306
|
-
|
|
307
|
-
|
|
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 &&
|
|
310
|
-
console.log(`[Auto Fetch] for ${cacheKey}. Fetching...`);
|
|
310
|
+
} else if (enabled && currentState.isStale && !currentState.loading) {
|
|
311
311
|
refetch();
|
|
312
|
-
} else if (enabled &&
|
|
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
|
-
|
|
317
|
-
}, [cacheKey, enabled, state
|
|
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 = { ...
|
|
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
|
-
|
|
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
|
-
|
|
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 = { ...
|
|
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,
|
|
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" ?
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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 >
|
|
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 =
|
|
516
|
-
return
|
|
491
|
+
const dehydrate = useCallback3(() => {
|
|
492
|
+
return globalStateManager.dehydrate();
|
|
517
493
|
}, []);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
494
|
+
return useMemo3(() => ({
|
|
495
|
+
actions,
|
|
496
|
+
states,
|
|
497
|
+
queries,
|
|
498
|
+
dehydrate,
|
|
521
499
|
...extraContextData || {}
|
|
522
|
-
}), [
|
|
523
|
-
return finalApiReturn;
|
|
500
|
+
}), [actions, states, queries, dehydrate, extraContextData]);
|
|
524
501
|
}
|
|
525
502
|
|
|
526
503
|
// src/hooks/useApiModule/apiModuleContext.ts
|