api-core-lib 12.12.111 → 16.2.0

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/client.js DELETED
@@ -1,535 +0,0 @@
1
- "use client";
2
- import {
3
- buildPaginateQuery,
4
- callDynamicApi,
5
- createApiServices,
6
- generateCacheKey,
7
- globalStateManager
8
- } from "./chunk-N7HFSKII.js";
9
-
10
- // src/hooks/useApi.ts
11
- import { useState, useEffect, useCallback, useRef, useMemo } from "react";
12
- function buildDynamicPath(template, params) {
13
- if (!params) return template;
14
- let path = template;
15
- for (const key in params) {
16
- path = path.replace(new RegExp(`{${key}}`, "g"), String(params[key]));
17
- }
18
- return path;
19
- }
20
- function useApi(axiosInstance, config) {
21
- const {
22
- endpoint,
23
- pathParams,
24
- initialData,
25
- initialQuery = {},
26
- enabled = true,
27
- refetchAfterChange = true,
28
- requestConfig,
29
- onSuccess,
30
- onError
31
- } = config;
32
- const finalEndpoint = useMemo(
33
- () => buildDynamicPath(endpoint, pathParams),
34
- [endpoint, pathParams]
35
- );
36
- const [state, setState] = useState({
37
- data: initialData ?? null,
38
- rawResponse: null,
39
- loading: enabled,
40
- error: null,
41
- success: false,
42
- message: void 0,
43
- validationErrors: void 0
44
- });
45
- const [queryOptions, setQueryOptions] = useState(initialQuery);
46
- const apiServices = useMemo(
47
- () => createApiServices(axiosInstance, finalEndpoint),
48
- [axiosInstance, finalEndpoint]
49
- );
50
- const savedOnSuccess = useRef(onSuccess);
51
- const savedOnError = useRef(onError);
52
- useEffect(() => {
53
- savedOnSuccess.current = onSuccess;
54
- savedOnError.current = onError;
55
- }, [onSuccess, onError]);
56
- const fetchData = useCallback(async (currentQueryOptions) => {
57
- setState((prev) => ({ ...prev, loading: true, error: null }));
58
- try {
59
- const finalQuery = currentQueryOptions || queryOptions;
60
- const hasQueryParams = Object.keys(finalQuery).length > 0;
61
- const queryString = buildPaginateQuery(finalQuery);
62
- const result = hasQueryParams ? await apiServices.getWithQuery(queryString, requestConfig) : await apiServices.get(void 0, requestConfig);
63
- setState(result);
64
- if (result.success && savedOnSuccess.current) {
65
- savedOnSuccess.current(result.message || "Fetch successful!", result.data ?? void 0);
66
- } else if (!result.success && savedOnError.current) {
67
- savedOnError.current(result.message || "Fetch failed", result.error ?? void 0);
68
- }
69
- } catch (e) {
70
- const apiError = { status: 500, message: e.message || "An unexpected client-side error occurred." };
71
- setState((prev) => ({ ...prev, loading: false, error: apiError, success: false }));
72
- if (savedOnError.current) {
73
- savedOnError.current(apiError.message, apiError);
74
- }
75
- }
76
- }, [apiServices, queryOptions, requestConfig]);
77
- useEffect(() => {
78
- if (enabled) {
79
- fetchData();
80
- }
81
- }, [enabled, fetchData]);
82
- const handleActionResult = useCallback(async (actionPromise) => {
83
- setState((prev) => ({ ...prev, loading: true }));
84
- try {
85
- const result = await actionPromise;
86
- if (result.success) {
87
- if (savedOnSuccess.current) {
88
- savedOnSuccess.current(result.message || "Action successful!", result.data ?? void 0);
89
- }
90
- if (refetchAfterChange) {
91
- await fetchData(queryOptions);
92
- } else {
93
- setState((prev) => ({ ...prev, ...result, loading: false }));
94
- }
95
- } else {
96
- if (savedOnError.current) {
97
- savedOnError.current(result.message || "Action failed", result.error ?? void 0);
98
- }
99
- setState((prev) => ({ ...prev, loading: false, error: result.error, success: false }));
100
- }
101
- return result;
102
- } catch (e) {
103
- const apiError = { status: 500, message: e.message || "An unexpected error occurred during action." };
104
- setState((prev) => ({ ...prev, loading: false, error: apiError, success: false }));
105
- if (savedOnError.current) {
106
- savedOnError.current(apiError.message, apiError);
107
- }
108
- return { data: null, error: apiError, loading: false, success: false, rawResponse: e };
109
- }
110
- }, [refetchAfterChange, fetchData, queryOptions]);
111
- const refetch = useCallback(() => {
112
- fetchData();
113
- }, [fetchData]);
114
- const actions = {
115
- fetch: fetchData,
116
- create: (newItem, options) => handleActionResult(apiServices.post(newItem, options)),
117
- put: (id, item, options) => handleActionResult(apiServices.put(id, item, options)),
118
- update: (id, updatedItem, options) => handleActionResult(apiServices.patch(id, updatedItem, options)),
119
- remove: (id, options) => handleActionResult(apiServices.remove(id, options)),
120
- bulkRemove: (ids, options) => handleActionResult(apiServices.bulkDelete(ids, options))
121
- };
122
- const query = {
123
- options: queryOptions,
124
- refetch,
125
- setOptions: setQueryOptions,
126
- setPage: (page) => setQueryOptions((prev) => ({ ...prev, page })),
127
- setLimit: (limit) => setQueryOptions((prev) => ({ ...prev, page: 1, limit })),
128
- setSearchTerm: (search) => setQueryOptions((prev) => ({ ...prev, page: 1, search })),
129
- setSorting: (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy })),
130
- setFilters: (filter) => setQueryOptions((prev) => ({ ...prev, page: 1, filter })),
131
- setQueryParam: (key, value) => setQueryOptions((prev) => ({ ...prev, [key]: value, page: key !== "page" ? 1 : value })),
132
- reset: () => setQueryOptions(initialQuery)
133
- };
134
- return { state, setState, actions, query };
135
- }
136
-
137
- // src/hooks/useApiRecord/index.ts
138
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2, useMemo as useMemo2 } from "react";
139
- function buildDynamicPath2(template, params) {
140
- if (!params) return template;
141
- let path = template;
142
- for (const key in params) {
143
- path = path.replace(new RegExp(`{${key}}`, "g"), String(params[key]));
144
- }
145
- return path;
146
- }
147
- function useApiRecord(axiosInstance, config) {
148
- const {
149
- endpoint,
150
- pathParams,
151
- recordId,
152
- initialData,
153
- enabled = true,
154
- refetchAfterChange = true,
155
- requestConfig,
156
- onSuccess,
157
- onError
158
- } = config;
159
- const finalEndpoint = useMemo2(
160
- () => buildDynamicPath2(endpoint, pathParams),
161
- [endpoint, pathParams]
162
- );
163
- const initialState = useMemo2(() => ({
164
- data: initialData ?? null,
165
- rawResponse: null,
166
- error: null,
167
- loading: enabled && !!recordId,
168
- success: false,
169
- message: void 0,
170
- validationErrors: void 0
171
- }), [initialData, enabled, recordId]);
172
- const [state, setState] = useState2(initialState);
173
- const savedOnSuccess = useRef2(onSuccess);
174
- const savedOnError = useRef2(onError);
175
- useEffect2(() => {
176
- savedOnSuccess.current = onSuccess;
177
- savedOnError.current = onError;
178
- }, [onSuccess, onError]);
179
- const apiServices = useMemo2(
180
- () => createApiServices(axiosInstance, finalEndpoint),
181
- [axiosInstance, finalEndpoint]
182
- );
183
- const fetchRecord = useCallback2(async () => {
184
- if (!recordId) {
185
- setState(initialState);
186
- return;
187
- }
188
- setState((prev) => ({ ...prev, loading: true, error: null, success: false }));
189
- try {
190
- const result = await apiServices.get(recordId, requestConfig);
191
- setState(result);
192
- if (result.success && savedOnSuccess.current) {
193
- savedOnSuccess.current(result.message || "Record fetched successfully!", result.data ?? void 0);
194
- } else if (!result.success && savedOnError.current) {
195
- savedOnError.current(result.message || "Fetch failed", result.error ?? void 0);
196
- }
197
- } catch (e) {
198
- console.error("[useApiRecord Fetch Error]", e);
199
- const apiError = {
200
- status: e.response?.status || 500,
201
- message: e.message || "An unexpected client-side error occurred.",
202
- code: e.code
203
- };
204
- setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));
205
- if (savedOnError.current) {
206
- savedOnError.current(apiError.message, apiError);
207
- }
208
- }
209
- }, [apiServices, recordId, requestConfig, initialState]);
210
- useEffect2(() => {
211
- if (enabled) {
212
- fetchRecord();
213
- }
214
- }, [enabled, fetchRecord]);
215
- const handleActionResult = useCallback2(
216
- async (actionPromise, options) => {
217
- setState((prev) => ({ ...prev, loading: true }));
218
- try {
219
- const result = await actionPromise;
220
- if (result.success) {
221
- if (savedOnSuccess.current) {
222
- savedOnSuccess.current(result.message || "Action successful!", result.data);
223
- }
224
- const shouldRefetch = options?.refetch ?? refetchAfterChange;
225
- if (shouldRefetch) {
226
- await fetchRecord();
227
- } else {
228
- setState({ ...result, loading: false, data: result.data });
229
- }
230
- } else {
231
- if (savedOnError.current) {
232
- savedOnError.current(result.message || "Action failed", result.error ?? void 0);
233
- }
234
- setState((prev) => ({ ...prev, ...result, loading: false }));
235
- }
236
- return result;
237
- } catch (e) {
238
- console.error("[useApiRecord Action Error]", e);
239
- const apiError = {
240
- status: e.response?.status || 500,
241
- message: e.message || "An unexpected error occurred during the action.",
242
- code: e.code
243
- };
244
- const errorResponse = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };
245
- setState(errorResponse);
246
- if (savedOnError.current) {
247
- savedOnError.current(apiError.message, apiError);
248
- }
249
- return errorResponse;
250
- }
251
- },
252
- [refetchAfterChange, fetchRecord, initialState]
253
- );
254
- const actions = {
255
- fetch: fetchRecord,
256
- update: (updatedItem, options) => handleActionResult(apiServices.patch(recordId, updatedItem, { ...requestConfig, ...options }), options),
257
- put: (item, options) => handleActionResult(apiServices.put(recordId, item, { ...requestConfig, ...options }), options),
258
- remove: (options) => handleActionResult(apiServices.remove(recordId, { ...requestConfig, ...options }), options),
259
- resetState: () => setState(initialState)
260
- };
261
- return { state, setState, actions };
262
- }
263
-
264
- // src/hooks/useDeepCompareEffect/index.ts
265
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
266
- import isEqual from "fast-deep-equal";
267
- function useDeepCompareEffect(callback, dependencies) {
268
- const currentDependenciesRef = useRef3();
269
- if (!isEqual(currentDependenciesRef.current, dependencies)) {
270
- currentDependenciesRef.current = dependencies;
271
- }
272
- useEffect3(callback, [currentDependenciesRef.current]);
273
- }
274
-
275
- // src/hooks/useApiModule/useApiModule.ts
276
- import { createContext, useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3, useSyncExternalStore } from "react";
277
- var ApiModuleContext = createContext(null);
278
- var ApiModuleProvider = ApiModuleContext.Provider;
279
- var createInitialState = () => ({
280
- data: null,
281
- lastSuccessAt: void 0,
282
- meta: [],
283
- validationErrors: [],
284
- error: null,
285
- loading: false,
286
- success: false,
287
- called: false,
288
- isStale: false,
289
- rawResponse: null
290
- });
291
- function useApiActionState(actionConfig, cacheKey, execute, input, enabled) {
292
- const getClientSnapshot = () => globalStateManager.getSnapshot(cacheKey);
293
- const getServerSnapshot = () => globalStateManager.getSnapshot(cacheKey);
294
- const state = useSyncExternalStore(
295
- (callback) => globalStateManager.subscribe(cacheKey, callback),
296
- getClientSnapshot,
297
- getServerSnapshot
298
- );
299
- const inputRef = useRef4(input);
300
- useEffect4(() => {
301
- inputRef.current = input;
302
- }, [input]);
303
- const refetch = useCallback3(() => {
304
- execute(inputRef.current);
305
- }, [execute]);
306
- const prevCacheKeyRef = useRef4(cacheKey);
307
- useEffect4(() => {
308
- if (prevCacheKeyRef.current !== cacheKey && enabled && state.called) {
309
- refetch();
310
- } else if (enabled && actionConfig.autoFetch && !state.called && !state.loading) {
311
- refetch();
312
- } else if (enabled && state.isStale && !state.loading) {
313
- refetch();
314
- }
315
- prevCacheKeyRef.current = cacheKey;
316
- }, [cacheKey, enabled, state.isStale, state.loading, state.called, actionConfig.autoFetch, refetch]);
317
- return state;
318
- }
319
- function useApiModule(axiosInstance, moduleConfig, options = {}) {
320
- const {
321
- refetchOnWindowFocus = true,
322
- onSuccess,
323
- onError,
324
- pathParams: modulePathParams,
325
- enabled = true,
326
- hydratedState,
327
- extraContextData
328
- } = options;
329
- const pathParamsString = useMemo3(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);
330
- const [queryOptions, setQueryOptions] = useState3({});
331
- const savedCallbacks = useRef4({ onSuccess, onError });
332
- useEffect4(() => {
333
- savedCallbacks.current = { onSuccess, onError };
334
- }, [onSuccess, onError]);
335
- useMemo3(() => {
336
- if (hydratedState) {
337
- globalStateManager.rehydrate(hydratedState);
338
- }
339
- }, [hydratedState]);
340
- const actions = useMemo3(() => {
341
- return Object.keys(moduleConfig.actions).reduce((acc, actionName) => {
342
- const actionConfig = moduleConfig.actions[actionName];
343
- const shouldCache = actionConfig.cacheResponse !== false;
344
- const execute = async (input, options2 = {}) => {
345
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
346
- const cacheKey = shouldCache ? generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams }) : "";
347
- let mutationContext;
348
- try {
349
- if (options2.onMutate && shouldCache) {
350
- mutationContext = await options2.onMutate(input);
351
- }
352
- if (shouldCache) {
353
- globalStateManager.setState(cacheKey, (prev) => ({ ...prev, loading: true, called: true, error: null, isStale: false }));
354
- }
355
- const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {
356
- pathParams: finalPathParams,
357
- body: input,
358
- config: options2.config
359
- });
360
- if (shouldCache) {
361
- globalStateManager.setState(cacheKey, (prev) => ({
362
- ...prev,
363
- loading: false,
364
- success: result.success,
365
- error: result.success ? null : result.error || prev.error,
366
- data: result.data,
367
- meta: result.meta,
368
- links: result.links,
369
- message: result.message,
370
- validationErrors: result.validationErrors || [],
371
- rawResponse: result.rawResponse
372
- }));
373
- }
374
- if (result.success) {
375
- savedCallbacks.current.onSuccess?.(actionName, result.message || "Action successful", result.data);
376
- options2.onSuccess?.(result.data, mutationContext);
377
- actionConfig.invalidates?.forEach((keyToInvalidate) => {
378
- const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;
379
- globalStateManager.invalidateByPrefix(prefix);
380
- });
381
- } else {
382
- savedCallbacks.current.onError?.(actionName, result.message || "Action failed", result.error ?? void 0);
383
- options2.onError?.(result.error, mutationContext);
384
- }
385
- return result;
386
- } catch (error) {
387
- const apiError = error.response?.data || { status: 500, message: error.message };
388
- if (shouldCache) {
389
- globalStateManager.setState(cacheKey, (prev) => ({ ...prev, error: apiError, loading: false, success: false }));
390
- }
391
- savedCallbacks.current.onError?.(actionName, apiError.message, apiError);
392
- options2.onError?.(apiError, mutationContext);
393
- throw error;
394
- } finally {
395
- if (options2.onSettled) {
396
- options2.onSettled();
397
- }
398
- }
399
- };
400
- const reset = (input, options2 = {}) => {
401
- if (shouldCache) {
402
- const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
403
- const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams });
404
- globalStateManager.setState(cacheKey, () => createInitialState());
405
- }
406
- };
407
- acc[actionName] = { execute, reset };
408
- return acc;
409
- }, {});
410
- }, [axiosInstance, moduleConfig, pathParamsString]);
411
- const queries = useMemo3(() => {
412
- return Object.keys(moduleConfig.actions).reduce((acc, actionName) => {
413
- const actionConfig = moduleConfig.actions[actionName];
414
- if (actionConfig?.hasQuery) {
415
- const setActionQueryOptions = (updater) => {
416
- const key = actionName;
417
- setQueryOptions((prev) => ({ ...prev, [key]: typeof updater === "function" ? updater(prev[key] || {}) : updater }));
418
- };
419
- const specificActionExecute = actions[actionName].execute;
420
- const refetch = () => {
421
- specificActionExecute(queryOptions[actionName] || {});
422
- };
423
- acc[actionName] = {
424
- options: queryOptions[actionName] || {},
425
- refetch,
426
- setOptions: setActionQueryOptions,
427
- setPage: (page) => setActionQueryOptions((p) => ({ ...p, page })),
428
- setLimit: (limit) => setActionQueryOptions((p) => ({ ...p, limit, page: 1 })),
429
- setSearchTerm: (search) => setActionQueryOptions((p) => ({ ...p, search, page: 1 })),
430
- setFilters: (filter) => setActionQueryOptions((p) => ({ ...p, filter, page: 1 })),
431
- setSorting: (sortBy) => setActionQueryOptions((p) => ({ ...p, sortBy })),
432
- setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? value : p.page })),
433
- reset: () => setActionQueryOptions({})
434
- };
435
- }
436
- return acc;
437
- }, {});
438
- }, [actions, queryOptions, moduleConfig.actions]);
439
- const states = {};
440
- function isActionWithQuery(key, actions2) {
441
- return actions2[key]?.hasQuery === true;
442
- }
443
- for (const actionName in moduleConfig.actions) {
444
- if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
445
- const actionConfig = moduleConfig.actions[actionName];
446
- if (actionConfig.cacheResponse !== false) {
447
- let currentQueryOptions;
448
- if (isActionWithQuery(actionName, moduleConfig.actions)) {
449
- currentQueryOptions = queries[actionName]?.options;
450
- }
451
- const input = currentQueryOptions;
452
- const pathParams = JSON.parse(pathParamsString);
453
- const cacheKey = generateCacheKey(
454
- moduleConfig.baseEndpoint,
455
- actionName,
456
- input,
457
- { pathParams }
458
- );
459
- states[actionName] = useApiActionState(
460
- actionConfig,
461
- cacheKey,
462
- actions[actionName].execute,
463
- input,
464
- enabled
465
- );
466
- } else {
467
- states[actionName] = createInitialState();
468
- }
469
- }
470
- }
471
- const lastBlurTimestamp = useRef4(Date.now());
472
- useEffect4(() => {
473
- if (!enabled || !refetchOnWindowFocus) return;
474
- const onFocus = () => {
475
- if (Date.now() - lastBlurTimestamp.current > 1e4) {
476
- const actionKeys = Object.keys(moduleConfig.actions);
477
- for (const actionName of actionKeys) {
478
- const state = states[actionName];
479
- if (state && state.called && !state.loading) {
480
- const prefix = `${moduleConfig.baseEndpoint}/${actionName}::`;
481
- globalStateManager.invalidateByPrefix(prefix);
482
- }
483
- }
484
- }
485
- };
486
- const onBlur = () => {
487
- lastBlurTimestamp.current = Date.now();
488
- };
489
- window.addEventListener("focus", onFocus);
490
- window.addEventListener("blur", onBlur);
491
- return () => {
492
- window.removeEventListener("focus", onFocus);
493
- window.removeEventListener("blur", onBlur);
494
- };
495
- }, [enabled, refetchOnWindowFocus, moduleConfig, states]);
496
- const dehydrate = useMemo3(() => {
497
- return () => globalStateManager.dehydrate();
498
- }, []);
499
- const baseApiReturn = { actions, states, queries, dehydrate };
500
- const finalApiReturn = useMemo3(() => ({
501
- ...baseApiReturn,
502
- ...extraContextData || {}
503
- }), [baseApiReturn, extraContextData]);
504
- return finalApiReturn;
505
- }
506
-
507
- // src/hooks/useApiModule/apiModuleContext.ts
508
- import { createContext as createContext2, useContext as useContext2, createElement } from "react";
509
- function createApiModuleContext() {
510
- const Context = createContext2(null);
511
- Context.displayName = "ApiModuleContext";
512
- const Provider = ({ value, children }) => {
513
- return createElement(Context.Provider, { value }, children);
514
- };
515
- const useConsumer = () => {
516
- const context = useContext2(Context);
517
- if (!context) {
518
- throw new Error("This component must be used within its corresponding ApiModuleProvider.");
519
- }
520
- return context;
521
- };
522
- return {
523
- Context,
524
- Provider,
525
- useContext: useConsumer
526
- };
527
- }
528
- export {
529
- ApiModuleProvider,
530
- createApiModuleContext,
531
- useApi,
532
- useApiModule,
533
- useApiRecord,
534
- useDeepCompareEffect
535
- };