api-core-lib 12.0.10 → 12.0.12
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/{apiModule.types-2yhaJWN3.d.cts → apiModule.types-CpwGDEpG.d.cts} +2 -2
- package/dist/{apiModule.types-2yhaJWN3.d.ts → apiModule.types-CpwGDEpG.d.ts} +2 -2
- package/dist/client.cjs +869 -0
- package/dist/client.d.cts +28 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +829 -0
- package/dist/index.cjs +2 -458
- package/dist/index.d.cts +5 -113
- package/dist/index.d.ts +5 -113
- package/dist/index.js +1 -451
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/useApiRecord.types-B45E0qux.d.cts +90 -0
- package/dist/useApiRecord.types-DlrlXL1t.d.ts +90 -0
- package/package.json +7 -2
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from))
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
32
|
+
|
|
33
|
+
// src/client.ts
|
|
34
|
+
var client_exports = {};
|
|
35
|
+
__export(client_exports, {
|
|
36
|
+
ApiModuleProvider: () => ApiModuleProvider,
|
|
37
|
+
useApi: () => useApi,
|
|
38
|
+
useApiModule: () => useApiModule,
|
|
39
|
+
useApiRecord: () => useApiRecord,
|
|
40
|
+
useDeepCompareEffect: () => useDeepCompareEffect,
|
|
41
|
+
useModuleContext: () => useModuleContext
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(client_exports);
|
|
44
|
+
|
|
45
|
+
// src/hooks/useApi.ts
|
|
46
|
+
var import_react = require("react");
|
|
47
|
+
|
|
48
|
+
// src/core/processor.ts
|
|
49
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
50
|
+
|
|
51
|
+
// src/core/utils.ts
|
|
52
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
53
|
+
function isAxiosResponse(obj) {
|
|
54
|
+
return obj && obj.data !== void 0 && obj.status !== void 0 && obj.config !== void 0;
|
|
55
|
+
}
|
|
56
|
+
function buildPaginateQuery(options) {
|
|
57
|
+
const params = new URLSearchParams();
|
|
58
|
+
for (const key in options) {
|
|
59
|
+
const value = options[key];
|
|
60
|
+
if (value === null || value === void 0) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key === "filter" && typeof value === "object" && !Array.isArray(value)) {
|
|
64
|
+
for (const filterKey in value) {
|
|
65
|
+
const filterValue = value[filterKey];
|
|
66
|
+
if (filterValue !== null && filterValue !== void 0) {
|
|
67
|
+
params.append(`filter[${filterKey}]`, String(filterValue));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else if (key === "sortBy" && Array.isArray(value)) {
|
|
71
|
+
value.forEach((sortItem) => {
|
|
72
|
+
if (sortItem && sortItem.key && sortItem.direction) {
|
|
73
|
+
params.append("sortBy[]", `${sortItem.key}:${sortItem.direction}`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
params.append(key, String(value));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return params.toString();
|
|
81
|
+
}
|
|
82
|
+
function isServerError(error) {
|
|
83
|
+
return import_axios.default.isAxiosError(error) && error.response !== void 0;
|
|
84
|
+
}
|
|
85
|
+
function isNetworkError(error) {
|
|
86
|
+
return import_axios.default.isAxiosError(error) && error.response === void 0 && error.request !== void 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/core/processor.ts
|
|
90
|
+
var processResponse = (responseOrError) => {
|
|
91
|
+
if (isAxiosResponse(responseOrError)) {
|
|
92
|
+
const response = responseOrError;
|
|
93
|
+
const rawData = response.data;
|
|
94
|
+
const isStandardApiResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
|
|
95
|
+
return {
|
|
96
|
+
data: isStandardApiResponse ? rawData.data : rawData,
|
|
97
|
+
links: isStandardApiResponse ? rawData.links : void 0,
|
|
98
|
+
meta: isStandardApiResponse ? rawData.meta : void 0,
|
|
99
|
+
rawResponse: rawData,
|
|
100
|
+
loading: false,
|
|
101
|
+
success: true,
|
|
102
|
+
error: null,
|
|
103
|
+
message: isStandardApiResponse ? rawData.message : "Request successful.",
|
|
104
|
+
validationErrors: []
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (isServerError(responseOrError)) {
|
|
108
|
+
const error = responseOrError;
|
|
109
|
+
const responseData = error.response.data;
|
|
110
|
+
const status = error.response.status;
|
|
111
|
+
let defaultMessage = "An unexpected server error occurred.";
|
|
112
|
+
switch (status) {
|
|
113
|
+
case 400:
|
|
114
|
+
defaultMessage = "Bad Request";
|
|
115
|
+
break;
|
|
116
|
+
case 401:
|
|
117
|
+
defaultMessage = "Unauthorized";
|
|
118
|
+
break;
|
|
119
|
+
case 403:
|
|
120
|
+
defaultMessage = "Forbidden";
|
|
121
|
+
break;
|
|
122
|
+
case 404:
|
|
123
|
+
defaultMessage = "Not Found";
|
|
124
|
+
break;
|
|
125
|
+
case 422:
|
|
126
|
+
defaultMessage = "Validation Failed";
|
|
127
|
+
break;
|
|
128
|
+
case 500:
|
|
129
|
+
defaultMessage = "Internal Server Error";
|
|
130
|
+
break;
|
|
131
|
+
case 503:
|
|
132
|
+
defaultMessage = "Service Unavailable";
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
defaultMessage = `Server Error (${status})`;
|
|
136
|
+
}
|
|
137
|
+
const finalApiError = {
|
|
138
|
+
message: responseData?.message || defaultMessage,
|
|
139
|
+
status,
|
|
140
|
+
code: responseData?.code || error.code,
|
|
141
|
+
errors: responseData?.errors || []
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
data: null,
|
|
145
|
+
rawResponse: responseData,
|
|
146
|
+
error: finalApiError,
|
|
147
|
+
validationErrors: finalApiError.errors,
|
|
148
|
+
success: false,
|
|
149
|
+
loading: false,
|
|
150
|
+
message: finalApiError.message
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (isNetworkError(responseOrError)) {
|
|
154
|
+
const error = responseOrError;
|
|
155
|
+
return {
|
|
156
|
+
data: null,
|
|
157
|
+
rawResponse: error.request,
|
|
158
|
+
error: { message: "Network Error: Unable to connect.", status: 0, code: error.code },
|
|
159
|
+
success: false,
|
|
160
|
+
loading: false,
|
|
161
|
+
message: "Network Error."
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (import_axios2.default.isCancel(responseOrError)) {
|
|
165
|
+
return {
|
|
166
|
+
data: null,
|
|
167
|
+
rawResponse: null,
|
|
168
|
+
error: { message: "Request Canceled.", status: 499 },
|
|
169
|
+
success: false,
|
|
170
|
+
loading: false,
|
|
171
|
+
message: "Request Canceled."
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
data: null,
|
|
176
|
+
rawResponse: responseOrError,
|
|
177
|
+
error: { message: "An unknown error occurred.", status: -1 },
|
|
178
|
+
success: false,
|
|
179
|
+
loading: false,
|
|
180
|
+
message: "An unknown error occurred."
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/core/buildDynamicUrl.ts
|
|
185
|
+
function buildDynamicUrl(template, params) {
|
|
186
|
+
if (!params) {
|
|
187
|
+
return template;
|
|
188
|
+
}
|
|
189
|
+
return template.replace(/\{(\w+)\}/g, (placeholder, key) => {
|
|
190
|
+
return params.hasOwnProperty(key) ? String(params[key]) : placeholder;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/services/crud.ts
|
|
195
|
+
function createApiServices(axiosInstance, baseEndpoint) {
|
|
196
|
+
const resolveUrl = (config = {}, id) => {
|
|
197
|
+
const endpointTemplate = config.endpoint || (id != null ? `${baseEndpoint}/{id}` : baseEndpoint);
|
|
198
|
+
const params = id != null ? { id, tenant: id, tenantId: id, recordId: id } : {};
|
|
199
|
+
return buildDynamicUrl(endpointTemplate, params);
|
|
200
|
+
};
|
|
201
|
+
const get = async (id, config) => {
|
|
202
|
+
const url = resolveUrl(config, id);
|
|
203
|
+
try {
|
|
204
|
+
const response = await axiosInstance.get(url, config);
|
|
205
|
+
return processResponse(response);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return processResponse(error);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const getWithQuery = async (query, config) => {
|
|
211
|
+
const url = `${baseEndpoint}?${query}`;
|
|
212
|
+
try {
|
|
213
|
+
const response = await axiosInstance.get(url, config);
|
|
214
|
+
return processResponse(response);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return processResponse(error);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const post = async (data, config) => {
|
|
220
|
+
const url = resolveUrl(config);
|
|
221
|
+
try {
|
|
222
|
+
const response = await axiosInstance.post(url, data, config);
|
|
223
|
+
console.log("[lib] response POST: ", response);
|
|
224
|
+
console.log("[lib] response processResponse POST: ", processResponse(response));
|
|
225
|
+
return processResponse(response);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.log("[lib] response error POST: ", processResponse(error));
|
|
228
|
+
return processResponse(error);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const put = async (id, data, config) => {
|
|
232
|
+
const url = resolveUrl(config, id);
|
|
233
|
+
try {
|
|
234
|
+
const response = await axiosInstance.put(url, data, config);
|
|
235
|
+
return processResponse(response);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return processResponse(error);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const patch = async (id, data, config) => {
|
|
241
|
+
const url = resolveUrl(config, id);
|
|
242
|
+
try {
|
|
243
|
+
const response = await axiosInstance.patch(url, data, config);
|
|
244
|
+
return processResponse(response);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return processResponse(error);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const remove = async (id, config) => {
|
|
250
|
+
const url = resolveUrl(config, id);
|
|
251
|
+
try {
|
|
252
|
+
const response = await axiosInstance.delete(url, config);
|
|
253
|
+
return processResponse(response);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return processResponse(error);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const bulkDelete = async (ids, config) => {
|
|
259
|
+
const url = resolveUrl(config);
|
|
260
|
+
try {
|
|
261
|
+
const response = await axiosInstance.delete(url, { data: { ids }, ...config });
|
|
262
|
+
return processResponse(response);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return processResponse(error);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const upload = async (file, additionalData, config) => {
|
|
268
|
+
const url = resolveUrl(config);
|
|
269
|
+
const formData = new FormData();
|
|
270
|
+
formData.append("file", file);
|
|
271
|
+
if (additionalData) {
|
|
272
|
+
Object.keys(additionalData).forEach((key) => formData.append(key, String(additionalData[key])));
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const response = await axiosInstance.post(url, formData, {
|
|
276
|
+
...config,
|
|
277
|
+
headers: { ...config?.headers, "Content-Type": "multipart/form-data" }
|
|
278
|
+
});
|
|
279
|
+
return processResponse(response);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return processResponse(error);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
return { get, getWithQuery, post, put, patch, remove, bulkDelete, upload };
|
|
285
|
+
}
|
|
286
|
+
async function callDynamicApi(axiosInstance, baseEndpoint, actionConfig, params) {
|
|
287
|
+
const { pathParams, body, config } = params;
|
|
288
|
+
const urlTemplate = `${baseEndpoint}${actionConfig.path}`;
|
|
289
|
+
const finalUrl = buildDynamicUrl(urlTemplate, pathParams || {});
|
|
290
|
+
try {
|
|
291
|
+
const { method } = actionConfig;
|
|
292
|
+
let response;
|
|
293
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
294
|
+
response = await axiosInstance[method.toLowerCase()](finalUrl, body, config);
|
|
295
|
+
} else if (method === "DELETE") {
|
|
296
|
+
response = await axiosInstance.delete(finalUrl, { data: body, ...config });
|
|
297
|
+
} else {
|
|
298
|
+
response = await axiosInstance.get(finalUrl, { params: body, ...config });
|
|
299
|
+
}
|
|
300
|
+
return processResponse(response);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
return processResponse(error);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/hooks/useApi.ts
|
|
307
|
+
function buildDynamicPath(template, params) {
|
|
308
|
+
if (!params) return template;
|
|
309
|
+
let path = template;
|
|
310
|
+
for (const key in params) {
|
|
311
|
+
path = path.replace(new RegExp(`{${key}}`, "g"), String(params[key]));
|
|
312
|
+
}
|
|
313
|
+
return path;
|
|
314
|
+
}
|
|
315
|
+
function useApi(axiosInstance, config) {
|
|
316
|
+
const {
|
|
317
|
+
endpoint,
|
|
318
|
+
pathParams,
|
|
319
|
+
// [REMOVE] لم نعد بحاجة لهذا الخيار
|
|
320
|
+
// encodeQuery = true,
|
|
321
|
+
initialData,
|
|
322
|
+
initialQuery = {},
|
|
323
|
+
enabled = true,
|
|
324
|
+
refetchAfterChange = true,
|
|
325
|
+
requestConfig,
|
|
326
|
+
onSuccess,
|
|
327
|
+
onError
|
|
328
|
+
} = config;
|
|
329
|
+
const finalEndpoint = (0, import_react.useMemo)(
|
|
330
|
+
() => buildDynamicPath(endpoint, pathParams),
|
|
331
|
+
[endpoint, pathParams]
|
|
332
|
+
);
|
|
333
|
+
const [state, setState] = (0, import_react.useState)({
|
|
334
|
+
data: initialData ?? null,
|
|
335
|
+
rawResponse: null,
|
|
336
|
+
loading: enabled,
|
|
337
|
+
error: null,
|
|
338
|
+
success: false,
|
|
339
|
+
message: void 0,
|
|
340
|
+
validationErrors: void 0
|
|
341
|
+
});
|
|
342
|
+
const [queryOptions, setQueryOptions] = (0, import_react.useState)(initialQuery);
|
|
343
|
+
const apiServices = (0, import_react.useMemo)(
|
|
344
|
+
() => createApiServices(axiosInstance, finalEndpoint),
|
|
345
|
+
[axiosInstance, finalEndpoint]
|
|
346
|
+
);
|
|
347
|
+
const savedOnSuccess = (0, import_react.useRef)(onSuccess);
|
|
348
|
+
const savedOnError = (0, import_react.useRef)(onError);
|
|
349
|
+
(0, import_react.useEffect)(() => {
|
|
350
|
+
savedOnSuccess.current = onSuccess;
|
|
351
|
+
savedOnError.current = onError;
|
|
352
|
+
}, [onSuccess, onError]);
|
|
353
|
+
const fetchData = (0, import_react.useCallback)(async (currentQueryOptions) => {
|
|
354
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
355
|
+
try {
|
|
356
|
+
const finalQuery = currentQueryOptions || queryOptions;
|
|
357
|
+
const hasQueryParams = Object.keys(finalQuery).length > 0;
|
|
358
|
+
const queryString = buildPaginateQuery(finalQuery);
|
|
359
|
+
const result = hasQueryParams ? await apiServices.getWithQuery(queryString, requestConfig) : await apiServices.get(void 0, requestConfig);
|
|
360
|
+
setState(result);
|
|
361
|
+
if (result.success && savedOnSuccess.current) {
|
|
362
|
+
savedOnSuccess.current(result.message || "Fetch successful!", result.data ?? void 0);
|
|
363
|
+
} else if (!result.success && savedOnError.current) {
|
|
364
|
+
savedOnError.current(result.message || "Fetch failed", result.error ?? void 0);
|
|
365
|
+
}
|
|
366
|
+
} catch (e) {
|
|
367
|
+
const apiError = { status: 500, message: e.message || "An unexpected client-side error occurred." };
|
|
368
|
+
setState((prev) => ({ ...prev, loading: false, error: apiError, success: false }));
|
|
369
|
+
if (savedOnError.current) {
|
|
370
|
+
savedOnError.current(apiError.message, apiError);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}, [apiServices, queryOptions, requestConfig]);
|
|
374
|
+
(0, import_react.useEffect)(() => {
|
|
375
|
+
if (enabled) {
|
|
376
|
+
fetchData();
|
|
377
|
+
}
|
|
378
|
+
}, [enabled, fetchData]);
|
|
379
|
+
const handleActionResult = (0, import_react.useCallback)(async (actionPromise) => {
|
|
380
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
381
|
+
try {
|
|
382
|
+
const result = await actionPromise;
|
|
383
|
+
if (result.success) {
|
|
384
|
+
if (savedOnSuccess.current) {
|
|
385
|
+
savedOnSuccess.current(result.message || "Action successful!", result.data ?? void 0);
|
|
386
|
+
}
|
|
387
|
+
if (refetchAfterChange) {
|
|
388
|
+
await fetchData(queryOptions);
|
|
389
|
+
} else {
|
|
390
|
+
setState((prev) => ({ ...prev, ...result, loading: false }));
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
if (savedOnError.current) {
|
|
394
|
+
savedOnError.current(result.message || "Action failed", result.error ?? void 0);
|
|
395
|
+
}
|
|
396
|
+
setState((prev) => ({ ...prev, loading: false, error: result.error, success: false }));
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
} catch (e) {
|
|
400
|
+
const apiError = { status: 500, message: e.message || "An unexpected error occurred during action." };
|
|
401
|
+
setState((prev) => ({ ...prev, loading: false, error: apiError, success: false }));
|
|
402
|
+
if (savedOnError.current) {
|
|
403
|
+
savedOnError.current(apiError.message, apiError);
|
|
404
|
+
}
|
|
405
|
+
return { data: null, error: apiError, loading: false, success: false, rawResponse: e };
|
|
406
|
+
}
|
|
407
|
+
}, [refetchAfterChange, fetchData, queryOptions]);
|
|
408
|
+
const actions = {
|
|
409
|
+
fetch: fetchData,
|
|
410
|
+
create: (newItem, options) => handleActionResult(apiServices.post(newItem, options)),
|
|
411
|
+
put: (id, item, options) => handleActionResult(apiServices.put(id, item, options)),
|
|
412
|
+
update: (id, updatedItem, options) => handleActionResult(apiServices.patch(id, updatedItem, options)),
|
|
413
|
+
remove: (id, options) => handleActionResult(apiServices.remove(id, options)),
|
|
414
|
+
bulkRemove: (ids, options) => handleActionResult(apiServices.bulkDelete(ids, options))
|
|
415
|
+
};
|
|
416
|
+
const query = {
|
|
417
|
+
options: queryOptions,
|
|
418
|
+
setOptions: setQueryOptions,
|
|
419
|
+
setPage: (page) => setQueryOptions((prev) => ({ ...prev, page })),
|
|
420
|
+
setLimit: (limit) => setQueryOptions((prev) => ({ ...prev, page: 1, limit })),
|
|
421
|
+
setSearchTerm: (search) => setQueryOptions((prev) => ({ ...prev, page: 1, search })),
|
|
422
|
+
setSorting: (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy })),
|
|
423
|
+
setFilters: (filter) => setQueryOptions((prev) => ({ ...prev, page: 1, filter })),
|
|
424
|
+
setQueryParam: (key, value) => setQueryOptions((prev) => ({ ...prev, [key]: value, page: key !== "page" ? 1 : value })),
|
|
425
|
+
reset: () => setQueryOptions(initialQuery)
|
|
426
|
+
};
|
|
427
|
+
return { state, setState, actions, query };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/hooks/useApiRecord/index.ts
|
|
431
|
+
var import_react2 = require("react");
|
|
432
|
+
function buildDynamicPath2(template, params) {
|
|
433
|
+
if (!params) return template;
|
|
434
|
+
let path = template;
|
|
435
|
+
for (const key in params) {
|
|
436
|
+
path = path.replace(new RegExp(`{${key}}`, "g"), String(params[key]));
|
|
437
|
+
}
|
|
438
|
+
return path;
|
|
439
|
+
}
|
|
440
|
+
function useApiRecord(axiosInstance, config) {
|
|
441
|
+
const {
|
|
442
|
+
endpoint,
|
|
443
|
+
pathParams,
|
|
444
|
+
recordId,
|
|
445
|
+
initialData,
|
|
446
|
+
enabled = true,
|
|
447
|
+
refetchAfterChange = true,
|
|
448
|
+
requestConfig,
|
|
449
|
+
onSuccess,
|
|
450
|
+
onError
|
|
451
|
+
} = config;
|
|
452
|
+
const finalEndpoint = (0, import_react2.useMemo)(
|
|
453
|
+
() => buildDynamicPath2(endpoint, pathParams),
|
|
454
|
+
[endpoint, pathParams]
|
|
455
|
+
);
|
|
456
|
+
const initialState = (0, import_react2.useMemo)(() => ({
|
|
457
|
+
data: initialData ?? null,
|
|
458
|
+
rawResponse: null,
|
|
459
|
+
error: null,
|
|
460
|
+
loading: enabled && !!recordId,
|
|
461
|
+
success: false,
|
|
462
|
+
message: void 0,
|
|
463
|
+
validationErrors: void 0
|
|
464
|
+
}), [initialData, enabled, recordId]);
|
|
465
|
+
const [state, setState] = (0, import_react2.useState)(initialState);
|
|
466
|
+
const savedOnSuccess = (0, import_react2.useRef)(onSuccess);
|
|
467
|
+
const savedOnError = (0, import_react2.useRef)(onError);
|
|
468
|
+
(0, import_react2.useEffect)(() => {
|
|
469
|
+
savedOnSuccess.current = onSuccess;
|
|
470
|
+
savedOnError.current = onError;
|
|
471
|
+
}, [onSuccess, onError]);
|
|
472
|
+
const apiServices = (0, import_react2.useMemo)(
|
|
473
|
+
() => createApiServices(axiosInstance, finalEndpoint),
|
|
474
|
+
[axiosInstance, finalEndpoint]
|
|
475
|
+
);
|
|
476
|
+
const fetchRecord = (0, import_react2.useCallback)(async () => {
|
|
477
|
+
if (!recordId) {
|
|
478
|
+
setState(initialState);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
setState((prev) => ({ ...prev, loading: true, error: null, success: false }));
|
|
482
|
+
try {
|
|
483
|
+
const result = await apiServices.get(recordId, requestConfig);
|
|
484
|
+
setState(result);
|
|
485
|
+
if (result.success && savedOnSuccess.current) {
|
|
486
|
+
savedOnSuccess.current(result.message || "Record fetched successfully!", result.data ?? void 0);
|
|
487
|
+
} else if (!result.success && savedOnError.current) {
|
|
488
|
+
savedOnError.current(result.message || "Fetch failed", result.error ?? void 0);
|
|
489
|
+
}
|
|
490
|
+
} catch (e) {
|
|
491
|
+
console.error("[useApiRecord Fetch Error]", e);
|
|
492
|
+
const apiError = {
|
|
493
|
+
status: e.response?.status || 500,
|
|
494
|
+
message: e.message || "An unexpected client-side error occurred.",
|
|
495
|
+
code: e.code
|
|
496
|
+
};
|
|
497
|
+
setState((prev) => ({ ...prev, loading: false, success: false, error: apiError }));
|
|
498
|
+
if (savedOnError.current) {
|
|
499
|
+
savedOnError.current(apiError.message, apiError);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}, [apiServices, recordId, requestConfig, initialState]);
|
|
503
|
+
(0, import_react2.useEffect)(() => {
|
|
504
|
+
if (enabled) {
|
|
505
|
+
fetchRecord();
|
|
506
|
+
}
|
|
507
|
+
}, [enabled, fetchRecord]);
|
|
508
|
+
const handleActionResult = (0, import_react2.useCallback)(
|
|
509
|
+
async (actionPromise, options) => {
|
|
510
|
+
setState((prev) => ({ ...prev, loading: true }));
|
|
511
|
+
try {
|
|
512
|
+
const result = await actionPromise;
|
|
513
|
+
if (result.success) {
|
|
514
|
+
if (savedOnSuccess.current) {
|
|
515
|
+
savedOnSuccess.current(result.message || "Action successful!", result.data);
|
|
516
|
+
}
|
|
517
|
+
const shouldRefetch = options?.refetch ?? refetchAfterChange;
|
|
518
|
+
if (shouldRefetch) {
|
|
519
|
+
await fetchRecord();
|
|
520
|
+
} else {
|
|
521
|
+
setState({ ...result, loading: false, data: result.data });
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
if (savedOnError.current) {
|
|
525
|
+
savedOnError.current(result.message || "Action failed", result.error ?? void 0);
|
|
526
|
+
}
|
|
527
|
+
setState((prev) => ({ ...prev, ...result, loading: false }));
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
} catch (e) {
|
|
531
|
+
console.error("[useApiRecord Action Error]", e);
|
|
532
|
+
const apiError = {
|
|
533
|
+
status: e.response?.status || 500,
|
|
534
|
+
message: e.message || "An unexpected error occurred during the action.",
|
|
535
|
+
code: e.code
|
|
536
|
+
};
|
|
537
|
+
const errorResponse = { ...initialState, loading: false, success: false, error: apiError, rawResponse: e, data: null };
|
|
538
|
+
setState(errorResponse);
|
|
539
|
+
if (savedOnError.current) {
|
|
540
|
+
savedOnError.current(apiError.message, apiError);
|
|
541
|
+
}
|
|
542
|
+
return errorResponse;
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
[refetchAfterChange, fetchRecord, initialState]
|
|
546
|
+
);
|
|
547
|
+
const actions = {
|
|
548
|
+
fetch: fetchRecord,
|
|
549
|
+
update: (updatedItem, options) => handleActionResult(apiServices.patch(recordId, updatedItem, { ...requestConfig, ...options }), options),
|
|
550
|
+
put: (item, options) => handleActionResult(apiServices.put(recordId, item, { ...requestConfig, ...options }), options),
|
|
551
|
+
remove: (options) => handleActionResult(apiServices.remove(recordId, { ...requestConfig, ...options }), options),
|
|
552
|
+
resetState: () => setState(initialState)
|
|
553
|
+
};
|
|
554
|
+
return { state, setState, actions };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/hooks/useDeepCompareEffect/index.ts
|
|
558
|
+
var import_react3 = require("react");
|
|
559
|
+
var import_fast_deep_equal = __toESM(require("fast-deep-equal"), 1);
|
|
560
|
+
function useDeepCompareEffect(callback, dependencies) {
|
|
561
|
+
const currentDependenciesRef = (0, import_react3.useRef)();
|
|
562
|
+
if (!(0, import_fast_deep_equal.default)(currentDependenciesRef.current, dependencies)) {
|
|
563
|
+
currentDependenciesRef.current = dependencies;
|
|
564
|
+
}
|
|
565
|
+
(0, import_react3.useEffect)(callback, [currentDependenciesRef.current]);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/hooks/useApiModule/useApiModule.ts
|
|
569
|
+
var import_react4 = require("react");
|
|
570
|
+
|
|
571
|
+
// src/core/globalStateManager.ts
|
|
572
|
+
var createInitialState = () => ({
|
|
573
|
+
data: null,
|
|
574
|
+
links: void 0,
|
|
575
|
+
meta: void 0,
|
|
576
|
+
error: null,
|
|
577
|
+
loading: false,
|
|
578
|
+
success: false,
|
|
579
|
+
called: false,
|
|
580
|
+
isStale: false,
|
|
581
|
+
message: void 0,
|
|
582
|
+
validationErrors: [],
|
|
583
|
+
rawResponse: null
|
|
584
|
+
});
|
|
585
|
+
var GlobalStateManager = class {
|
|
586
|
+
constructor() {
|
|
587
|
+
__publicField(this, "store", /* @__PURE__ */ new Map());
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* يحصل على لقطة (snapshot) للحالة الحالية لمفتاح معين.
|
|
591
|
+
*/
|
|
592
|
+
getSnapshot(key) {
|
|
593
|
+
return this.store.get(key)?.state ?? createInitialState();
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* يسجل دالة callback للاستماع إلى التغييرات على مفتاح معين.
|
|
597
|
+
* @returns دالة لإلغاء الاشتراك.
|
|
598
|
+
*/
|
|
599
|
+
subscribe(key, callback) {
|
|
600
|
+
if (!this.store.has(key)) {
|
|
601
|
+
this.store.set(key, { state: createInitialState(), listeners: /* @__PURE__ */ new Set() });
|
|
602
|
+
}
|
|
603
|
+
const item = this.store.get(key);
|
|
604
|
+
item.listeners.add(callback);
|
|
605
|
+
return () => {
|
|
606
|
+
item.listeners.delete(callback);
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* يحدّث الحالة لمفتاح معين ويقوم بإعلام جميع المشتركين.
|
|
611
|
+
*/
|
|
612
|
+
setState(key, updater) {
|
|
613
|
+
const currentState = this.getSnapshot(key);
|
|
614
|
+
const newState = updater(currentState);
|
|
615
|
+
if (!this.store.has(key)) {
|
|
616
|
+
this.store.set(key, { state: newState, listeners: /* @__PURE__ */ new Set() });
|
|
617
|
+
} else {
|
|
618
|
+
this.store.get(key).state = newState;
|
|
619
|
+
}
|
|
620
|
+
this.store.get(key).listeners.forEach((listener) => listener());
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* يجعل البيانات المرتبطة بمفتاح معين "قديمة" (stale).
|
|
624
|
+
*/
|
|
625
|
+
invalidate(key) {
|
|
626
|
+
const state = this.getSnapshot(key);
|
|
627
|
+
if (state.called) {
|
|
628
|
+
this.setState(key, (prev) => ({ ...prev, isStale: true }));
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* [نسخة محدثة وأكثر قوة]
|
|
633
|
+
* يجعل كل البيانات التي تبدأ بمفتاح معين "قديمة" (stale).
|
|
634
|
+
* @example invalidateByPrefix('myModule/list::') سيبطل كل صفحات القائمة.
|
|
635
|
+
*/
|
|
636
|
+
invalidateByPrefix(prefix) {
|
|
637
|
+
this.store.forEach((value, key) => {
|
|
638
|
+
if (key.startsWith(prefix)) {
|
|
639
|
+
const state = this.getSnapshot(key);
|
|
640
|
+
if (state.called) {
|
|
641
|
+
this.setState(key, (prev) => ({ ...prev, isStale: true }));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Serializes the current state of the query store into a JSON string.
|
|
648
|
+
* This is used on the server to pass the initial state to the client.
|
|
649
|
+
* @returns A JSON string representing the dehydrated state.
|
|
650
|
+
*/
|
|
651
|
+
dehydrate() {
|
|
652
|
+
const stateToPersist = {};
|
|
653
|
+
this.store.forEach((value, key) => {
|
|
654
|
+
if (value.state.called) {
|
|
655
|
+
stateToPersist[key] = value.state;
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
return JSON.stringify(stateToPersist);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Merges a dehydrated state object into the current store.
|
|
662
|
+
* This is used on the client to hydrate the state received from the server.
|
|
663
|
+
* @param hydratedState - A JSON string from the `dehydrate` method.
|
|
664
|
+
*/
|
|
665
|
+
rehydrate(hydratedState) {
|
|
666
|
+
try {
|
|
667
|
+
const parsedState = JSON.parse(hydratedState);
|
|
668
|
+
for (const key in parsedState) {
|
|
669
|
+
this.setState(key, () => parsedState[key]);
|
|
670
|
+
}
|
|
671
|
+
} catch (e) {
|
|
672
|
+
console.error("[api-core-lib] Failed to rehydrate state:", e);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
var globalStateManager = new GlobalStateManager();
|
|
677
|
+
|
|
678
|
+
// src/hooks/useApiModule/useApiModule.ts
|
|
679
|
+
var ApiModuleContext = (0, import_react4.createContext)(null);
|
|
680
|
+
var ApiModuleProvider = ApiModuleContext.Provider;
|
|
681
|
+
function useApiActionState(actionConfig, cacheKey, actionExecutor, enabled) {
|
|
682
|
+
const state = (0, import_react4.useSyncExternalStore)(
|
|
683
|
+
(callback) => globalStateManager.subscribe(cacheKey, callback),
|
|
684
|
+
() => globalStateManager.getSnapshot(cacheKey)
|
|
685
|
+
);
|
|
686
|
+
const input = actionConfig.hasQuery ? state.options : void 0;
|
|
687
|
+
(0, import_react4.useEffect)(() => {
|
|
688
|
+
if (enabled && actionConfig.autoFetch && !state.called && !state.loading) {
|
|
689
|
+
actionExecutor(input);
|
|
690
|
+
} else if (enabled && state.isStale && !state.loading) {
|
|
691
|
+
actionExecutor(input);
|
|
692
|
+
}
|
|
693
|
+
}, [enabled, state.isStale, state.loading, state.called, actionConfig.autoFetch, actionExecutor, input]);
|
|
694
|
+
return state;
|
|
695
|
+
}
|
|
696
|
+
function useModuleContext() {
|
|
697
|
+
const context = (0, import_react4.useContext)(ApiModuleContext);
|
|
698
|
+
if (!context) {
|
|
699
|
+
throw new Error("useModuleContext must be used within an ApiModuleProvider");
|
|
700
|
+
}
|
|
701
|
+
return context;
|
|
702
|
+
}
|
|
703
|
+
var generateCacheKey = (moduleName, actionName, input, callOptions = {}) => {
|
|
704
|
+
const params = { path: callOptions.pathParams, body: input };
|
|
705
|
+
try {
|
|
706
|
+
return `${moduleName}/${actionName}::${JSON.stringify(params)}`;
|
|
707
|
+
} catch (error) {
|
|
708
|
+
return `${moduleName}/${actionName}::${Date.now()}`;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
function useApiModule(axiosInstance, moduleConfig, options = {}) {
|
|
712
|
+
const { refetchOnWindowFocus = true, onSuccess, onError, pathParams: modulePathParams, enabled = true, hydratedState } = options;
|
|
713
|
+
const pathParamsString = (0, import_react4.useMemo)(() => JSON.stringify(modulePathParams || {}), [modulePathParams]);
|
|
714
|
+
const [queryOptions, setQueryOptions] = (0, import_react4.useState)({});
|
|
715
|
+
const savedCallbacks = (0, import_react4.useRef)({ onSuccess, onError });
|
|
716
|
+
(0, import_react4.useEffect)(() => {
|
|
717
|
+
savedCallbacks.current = { onSuccess, onError };
|
|
718
|
+
}, [onSuccess, onError]);
|
|
719
|
+
(0, import_react4.useMemo)(() => {
|
|
720
|
+
if (hydratedState) {
|
|
721
|
+
globalStateManager.rehydrate(hydratedState);
|
|
722
|
+
}
|
|
723
|
+
}, [hydratedState]);
|
|
724
|
+
const actions = (0, import_react4.useMemo)(() => {
|
|
725
|
+
return Object.keys(moduleConfig.actions).reduce((acc, actionName) => {
|
|
726
|
+
const actionConfig = moduleConfig.actions[actionName];
|
|
727
|
+
const execute = async (input, options2 = {}) => {
|
|
728
|
+
const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
|
|
729
|
+
const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams });
|
|
730
|
+
let mutationContext;
|
|
731
|
+
try {
|
|
732
|
+
if (options2.onMutate) {
|
|
733
|
+
mutationContext = await options2.onMutate(input);
|
|
734
|
+
}
|
|
735
|
+
globalStateManager.setState(cacheKey, (prev) => ({ ...prev, loading: true, called: true, error: null, isStale: false }));
|
|
736
|
+
const result = await callDynamicApi(axiosInstance, moduleConfig.baseEndpoint, actionConfig, {
|
|
737
|
+
pathParams: finalPathParams,
|
|
738
|
+
body: input,
|
|
739
|
+
config: options2.config
|
|
740
|
+
});
|
|
741
|
+
globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...result, loading: false }));
|
|
742
|
+
if (result.success) {
|
|
743
|
+
savedCallbacks.current.onSuccess?.(actionName, result.message || "Action successful", result.data);
|
|
744
|
+
options2.onSuccess?.(result.data, mutationContext);
|
|
745
|
+
actionConfig.invalidates?.forEach((keyToInvalidate) => {
|
|
746
|
+
const prefix = `${moduleConfig.baseEndpoint}/${keyToInvalidate}::`;
|
|
747
|
+
globalStateManager.invalidateByPrefix(prefix);
|
|
748
|
+
});
|
|
749
|
+
} else {
|
|
750
|
+
savedCallbacks.current.onError?.(actionName, result.message || "Action failed", result.error ?? void 0);
|
|
751
|
+
options2.onError?.(result.error, mutationContext);
|
|
752
|
+
}
|
|
753
|
+
return result;
|
|
754
|
+
} catch (error) {
|
|
755
|
+
const apiError = { status: 500, message: error.message };
|
|
756
|
+
const errorResult = { data: null, error: apiError, loading: false, success: false, rawResponse: error };
|
|
757
|
+
globalStateManager.setState(cacheKey, (prev) => ({ ...prev, ...errorResult, loading: false }));
|
|
758
|
+
savedCallbacks.current.onError?.(actionName, apiError.message, apiError);
|
|
759
|
+
options2.onError?.(apiError, mutationContext);
|
|
760
|
+
return errorResult;
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
const reset = (input, options2 = {}) => {
|
|
764
|
+
const finalPathParams = { ...JSON.parse(pathParamsString), ...options2.pathParams };
|
|
765
|
+
const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams: finalPathParams });
|
|
766
|
+
globalStateManager.setState(cacheKey, () => ({
|
|
767
|
+
data: null,
|
|
768
|
+
error: null,
|
|
769
|
+
loading: false,
|
|
770
|
+
success: false,
|
|
771
|
+
called: false,
|
|
772
|
+
isStale: false,
|
|
773
|
+
rawResponse: null
|
|
774
|
+
}));
|
|
775
|
+
};
|
|
776
|
+
acc[actionName] = { execute, reset };
|
|
777
|
+
return acc;
|
|
778
|
+
}, {});
|
|
779
|
+
}, [axiosInstance, moduleConfig, pathParamsString]);
|
|
780
|
+
const queries = (0, import_react4.useMemo)(() => {
|
|
781
|
+
const builtQueries = {};
|
|
782
|
+
for (const actionName in moduleConfig.actions) {
|
|
783
|
+
if (moduleConfig.actions[actionName]?.hasQuery) {
|
|
784
|
+
const setActionQueryOptions = (updater) => {
|
|
785
|
+
setQueryOptions((prev) => ({ ...prev, [actionName]: typeof updater === "function" ? updater(prev[actionName] || {}) : updater }));
|
|
786
|
+
};
|
|
787
|
+
builtQueries[actionName] = {
|
|
788
|
+
options: queryOptions[actionName] || {},
|
|
789
|
+
setOptions: setActionQueryOptions,
|
|
790
|
+
setPage: (page) => setActionQueryOptions((p) => ({ ...p, page })),
|
|
791
|
+
setLimit: (limit) => setActionQueryOptions((p) => ({ ...p, limit, page: 1 })),
|
|
792
|
+
setSearchTerm: (search) => setActionQueryOptions((p) => ({ ...p, search, page: 1 })),
|
|
793
|
+
setFilters: (filter) => setActionQueryOptions((p) => ({ ...p, filter, page: 1 })),
|
|
794
|
+
setSorting: (sortBy) => setActionQueryOptions((p) => ({ ...p, sortBy })),
|
|
795
|
+
setQueryParam: (key, value) => setActionQueryOptions((p) => ({ ...p, [key]: value, page: key !== "page" ? value : p.page })),
|
|
796
|
+
reset: () => setActionQueryOptions({})
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return builtQueries;
|
|
801
|
+
}, [queryOptions, moduleConfig.actions]);
|
|
802
|
+
const states = {};
|
|
803
|
+
for (const actionName in moduleConfig.actions) {
|
|
804
|
+
if (Object.prototype.hasOwnProperty.call(moduleConfig.actions, actionName)) {
|
|
805
|
+
const actionConfig = moduleConfig.actions[actionName];
|
|
806
|
+
const query = queries[actionName]?.options;
|
|
807
|
+
const input = actionConfig.hasQuery ? query : void 0;
|
|
808
|
+
const pathParams = JSON.parse(pathParamsString);
|
|
809
|
+
const cacheKey = generateCacheKey(moduleConfig.baseEndpoint, actionName, input, { pathParams });
|
|
810
|
+
states[actionName] = useApiActionState(
|
|
811
|
+
actionConfig,
|
|
812
|
+
cacheKey,
|
|
813
|
+
actions[actionName].execute,
|
|
814
|
+
enabled
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
(0, import_react4.useEffect)(() => {
|
|
819
|
+
if (!enabled) return;
|
|
820
|
+
const actionKeys = Object.keys(moduleConfig.actions);
|
|
821
|
+
for (const actionName of actionKeys) {
|
|
822
|
+
const state = states[actionName];
|
|
823
|
+
const actionConfig = moduleConfig.actions[actionName];
|
|
824
|
+
if (actionConfig.autoFetch && state && !state.called && !state.loading) {
|
|
825
|
+
const query = queries[actionName]?.options;
|
|
826
|
+
const input = actionConfig.hasQuery ? query : void 0;
|
|
827
|
+
actions[actionName].execute(input);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}, [enabled, moduleConfig, actions, states, queries]);
|
|
831
|
+
const lastBlurTimestamp = (0, import_react4.useRef)(Date.now());
|
|
832
|
+
(0, import_react4.useEffect)(() => {
|
|
833
|
+
if (!enabled || !refetchOnWindowFocus) return;
|
|
834
|
+
const onFocus = () => {
|
|
835
|
+
if (Date.now() - lastBlurTimestamp.current > 1e4) {
|
|
836
|
+
const actionKeys = Object.keys(moduleConfig.actions);
|
|
837
|
+
for (const actionName of actionKeys) {
|
|
838
|
+
const state = states[actionName];
|
|
839
|
+
if (state && state.called && !state.loading) {
|
|
840
|
+
const prefix = `${moduleConfig.baseEndpoint}/${actionName}::`;
|
|
841
|
+
globalStateManager.invalidateByPrefix(prefix);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
const onBlur = () => {
|
|
847
|
+
lastBlurTimestamp.current = Date.now();
|
|
848
|
+
};
|
|
849
|
+
window.addEventListener("focus", onFocus);
|
|
850
|
+
window.addEventListener("blur", onBlur);
|
|
851
|
+
return () => {
|
|
852
|
+
window.removeEventListener("focus", onFocus);
|
|
853
|
+
window.removeEventListener("blur", onBlur);
|
|
854
|
+
};
|
|
855
|
+
}, [enabled, refetchOnWindowFocus, moduleConfig, states]);
|
|
856
|
+
const dehydrate = (0, import_react4.useMemo)(() => {
|
|
857
|
+
return () => globalStateManager.dehydrate();
|
|
858
|
+
}, []);
|
|
859
|
+
return { actions, states, queries, dehydrate };
|
|
860
|
+
}
|
|
861
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
862
|
+
0 && (module.exports = {
|
|
863
|
+
ApiModuleProvider,
|
|
864
|
+
useApi,
|
|
865
|
+
useApiModule,
|
|
866
|
+
useApiRecord,
|
|
867
|
+
useDeepCompareEffect,
|
|
868
|
+
useModuleContext
|
|
869
|
+
});
|