@harishmano/react-enterprise-api 1.0.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/index.d.mts +280 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +696 -0
- package/dist/index.mjs +664 -0
- package/package.json +65 -0
- package/vite.config.ts +9 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
// src/createApiClient.ts
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
// src/resource.ts
|
|
5
|
+
import {
|
|
6
|
+
useQuery,
|
|
7
|
+
useMutation,
|
|
8
|
+
useQueryClient,
|
|
9
|
+
useInfiniteQuery,
|
|
10
|
+
keepPreviousData
|
|
11
|
+
} from "@tanstack/react-query";
|
|
12
|
+
|
|
13
|
+
// src/utils.ts
|
|
14
|
+
function buildQueryParams(options, defaultParams) {
|
|
15
|
+
var _a;
|
|
16
|
+
const params = { ...defaultParams };
|
|
17
|
+
if (!options) return params;
|
|
18
|
+
if (options.pagination) {
|
|
19
|
+
const { page, pageSize, sortBy, sortOrder } = options.pagination;
|
|
20
|
+
if (page !== void 0) params.page = page;
|
|
21
|
+
if (pageSize !== void 0) params.pageSize = pageSize;
|
|
22
|
+
if (sortBy) params.sortBy = sortBy;
|
|
23
|
+
if (sortOrder) params.sortOrder = sortOrder;
|
|
24
|
+
}
|
|
25
|
+
if (options.search) {
|
|
26
|
+
params.search = options.search;
|
|
27
|
+
if ((_a = options.searchFields) == null ? void 0 : _a.length) {
|
|
28
|
+
params.searchFields = options.searchFields.join(",");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (options.filters) {
|
|
32
|
+
if (Array.isArray(options.filters)) {
|
|
33
|
+
params.filters = JSON.stringify(options.filters);
|
|
34
|
+
} else {
|
|
35
|
+
Object.assign(params, options.filters);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return params;
|
|
39
|
+
}
|
|
40
|
+
function buildQueryKey(resource, options) {
|
|
41
|
+
const key = [resource];
|
|
42
|
+
if (options == null ? void 0 : options.pagination) key.push({ pagination: options.pagination });
|
|
43
|
+
if (options == null ? void 0 : options.search) key.push({ search: options.search });
|
|
44
|
+
if (options == null ? void 0 : options.filters) key.push({ filters: serializeFilters(options.filters) });
|
|
45
|
+
return key;
|
|
46
|
+
}
|
|
47
|
+
function serializeFilters(filters) {
|
|
48
|
+
if (!filters) return "";
|
|
49
|
+
return JSON.stringify(filters);
|
|
50
|
+
}
|
|
51
|
+
function triggerBrowserDownload(blob, filename, mimeType) {
|
|
52
|
+
const resolvedBlob = mimeType && blob.type !== mimeType ? new Blob([blob], { type: mimeType }) : blob;
|
|
53
|
+
const url = URL.createObjectURL(resolvedBlob);
|
|
54
|
+
const a = document.createElement("a");
|
|
55
|
+
a.href = url;
|
|
56
|
+
a.download = filename;
|
|
57
|
+
document.body.appendChild(a);
|
|
58
|
+
a.click();
|
|
59
|
+
document.body.removeChild(a);
|
|
60
|
+
URL.revokeObjectURL(url);
|
|
61
|
+
}
|
|
62
|
+
function sleep(ms) {
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/resource.ts
|
|
67
|
+
function createResource(client, name, resourceOptions = {}) {
|
|
68
|
+
const {
|
|
69
|
+
path = `/${name}`,
|
|
70
|
+
defaultParams,
|
|
71
|
+
transform,
|
|
72
|
+
staleTime: defaultStaleTime = 1e3 * 60,
|
|
73
|
+
optimistic = false
|
|
74
|
+
} = resourceOptions;
|
|
75
|
+
function applyTransform(data) {
|
|
76
|
+
return transform ? transform(data) : data;
|
|
77
|
+
}
|
|
78
|
+
function useList(options) {
|
|
79
|
+
var _a;
|
|
80
|
+
const params = buildQueryParams(options, defaultParams);
|
|
81
|
+
const queryKey = buildQueryKey(name, options);
|
|
82
|
+
return useQuery({
|
|
83
|
+
queryKey,
|
|
84
|
+
queryFn: async ({ signal }) => {
|
|
85
|
+
const res = await client.get(path, {
|
|
86
|
+
params,
|
|
87
|
+
signal,
|
|
88
|
+
...options == null ? void 0 : options.axiosConfig
|
|
89
|
+
});
|
|
90
|
+
return applyTransform(res.data);
|
|
91
|
+
},
|
|
92
|
+
staleTime: (_a = options == null ? void 0 : options.staleTime) != null ? _a : defaultStaleTime,
|
|
93
|
+
placeholderData: keepPreviousData,
|
|
94
|
+
enabled: options == null ? void 0 : options.enabled,
|
|
95
|
+
refetchInterval: options == null ? void 0 : options.refetchInterval
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function useGet(id, options) {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
return useQuery({
|
|
101
|
+
queryKey: [name, id],
|
|
102
|
+
queryFn: async ({ signal }) => {
|
|
103
|
+
const res = await client.get(`${path}/${id}`, {
|
|
104
|
+
params: buildQueryParams(options, defaultParams),
|
|
105
|
+
signal,
|
|
106
|
+
...options == null ? void 0 : options.axiosConfig
|
|
107
|
+
});
|
|
108
|
+
return applyTransform(res.data);
|
|
109
|
+
},
|
|
110
|
+
staleTime: (_a = options == null ? void 0 : options.staleTime) != null ? _a : defaultStaleTime,
|
|
111
|
+
enabled: (_b = options == null ? void 0 : options.enabled) != null ? _b : !!id,
|
|
112
|
+
refetchInterval: options == null ? void 0 : options.refetchInterval
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function usePaginatedList(pagination, options) {
|
|
116
|
+
var _a;
|
|
117
|
+
const mergedOptions = { ...options, pagination };
|
|
118
|
+
const params = buildQueryParams(mergedOptions, defaultParams);
|
|
119
|
+
const queryKey = buildQueryKey(`${name}/paginated`, mergedOptions);
|
|
120
|
+
return useQuery({
|
|
121
|
+
queryKey,
|
|
122
|
+
queryFn: async ({ signal }) => {
|
|
123
|
+
const res = await client.get(path, {
|
|
124
|
+
params,
|
|
125
|
+
signal,
|
|
126
|
+
...options == null ? void 0 : options.axiosConfig
|
|
127
|
+
});
|
|
128
|
+
return applyTransform(res.data);
|
|
129
|
+
},
|
|
130
|
+
staleTime: (_a = options == null ? void 0 : options.staleTime) != null ? _a : defaultStaleTime,
|
|
131
|
+
placeholderData: keepPreviousData,
|
|
132
|
+
enabled: options == null ? void 0 : options.enabled
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function useInfiniteList(options) {
|
|
136
|
+
var _a;
|
|
137
|
+
const baseParams = buildQueryParams(options, defaultParams);
|
|
138
|
+
return useInfiniteQuery({
|
|
139
|
+
queryKey: [...buildQueryKey(name, options), "infinite"],
|
|
140
|
+
queryFn: async ({ pageParam = 1, signal }) => {
|
|
141
|
+
var _a2;
|
|
142
|
+
const res = await client.get(path, {
|
|
143
|
+
params: { ...baseParams, page: pageParam, pageSize: (_a2 = options == null ? void 0 : options.pageSize) != null ? _a2 : 20 },
|
|
144
|
+
signal,
|
|
145
|
+
...options == null ? void 0 : options.axiosConfig
|
|
146
|
+
});
|
|
147
|
+
return applyTransform(res.data);
|
|
148
|
+
},
|
|
149
|
+
initialPageParam: 1,
|
|
150
|
+
getNextPageParam: (lastPage, allPages) => Array.isArray(lastPage) && lastPage.length > 0 ? allPages.length + 1 : void 0,
|
|
151
|
+
staleTime: (_a = options == null ? void 0 : options.staleTime) != null ? _a : defaultStaleTime,
|
|
152
|
+
enabled: options == null ? void 0 : options.enabled
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function useCursorList(pagination, options) {
|
|
156
|
+
var _a;
|
|
157
|
+
const baseParams = buildQueryParams(options, defaultParams);
|
|
158
|
+
return useInfiniteQuery({
|
|
159
|
+
queryKey: [...buildQueryKey(name, options), "cursor", pagination],
|
|
160
|
+
queryFn: async ({ pageParam, signal }) => {
|
|
161
|
+
var _a2;
|
|
162
|
+
const res = await client.get(path, {
|
|
163
|
+
params: {
|
|
164
|
+
...baseParams,
|
|
165
|
+
cursor: pageParam != null ? pageParam : pagination == null ? void 0 : pagination.cursor,
|
|
166
|
+
pageSize: (_a2 = pagination == null ? void 0 : pagination.pageSize) != null ? _a2 : 20,
|
|
167
|
+
sortBy: pagination == null ? void 0 : pagination.sortBy,
|
|
168
|
+
sortOrder: pagination == null ? void 0 : pagination.sortOrder
|
|
169
|
+
},
|
|
170
|
+
signal,
|
|
171
|
+
...options == null ? void 0 : options.axiosConfig
|
|
172
|
+
});
|
|
173
|
+
return applyTransform(res.data);
|
|
174
|
+
},
|
|
175
|
+
initialPageParam: null,
|
|
176
|
+
getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.nextCursor : void 0,
|
|
177
|
+
staleTime: (_a = options == null ? void 0 : options.staleTime) != null ? _a : defaultStaleTime,
|
|
178
|
+
enabled: options == null ? void 0 : options.enabled
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function useCreate(mutationOptions) {
|
|
182
|
+
const qc = useQueryClient();
|
|
183
|
+
return useMutation({
|
|
184
|
+
mutationFn: async (data) => {
|
|
185
|
+
const res = await client.post(path, data);
|
|
186
|
+
return applyTransform(res.data);
|
|
187
|
+
},
|
|
188
|
+
onMutate: optimistic ? async (newItem) => {
|
|
189
|
+
await qc.cancelQueries({ queryKey: [name] });
|
|
190
|
+
const prev = qc.getQueryData([name]);
|
|
191
|
+
qc.setQueryData([name], (old = []) => [
|
|
192
|
+
...old,
|
|
193
|
+
newItem
|
|
194
|
+
]);
|
|
195
|
+
return { prev };
|
|
196
|
+
} : void 0,
|
|
197
|
+
onError: (error, variables, context) => {
|
|
198
|
+
var _a;
|
|
199
|
+
const ctx = context;
|
|
200
|
+
if (optimistic && (ctx == null ? void 0 : ctx.prev)) qc.setQueryData([name], ctx.prev);
|
|
201
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onError) == null ? void 0 : _a.call(mutationOptions, error, variables);
|
|
202
|
+
},
|
|
203
|
+
onSuccess: (data, variables) => {
|
|
204
|
+
var _a;
|
|
205
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
206
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, data, variables);
|
|
207
|
+
},
|
|
208
|
+
onSettled: mutationOptions == null ? void 0 : mutationOptions.onSettled
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function useUpdate(mutationOptions) {
|
|
212
|
+
const qc = useQueryClient();
|
|
213
|
+
return useMutation({
|
|
214
|
+
mutationFn: async ({ id, data }) => {
|
|
215
|
+
const res = await client.put(`${path}/${id}`, data);
|
|
216
|
+
return applyTransform(res.data);
|
|
217
|
+
},
|
|
218
|
+
onMutate: optimistic ? async ({ id, data }) => {
|
|
219
|
+
await qc.cancelQueries({ queryKey: [name, id] });
|
|
220
|
+
const prev = qc.getQueryData([name, id]);
|
|
221
|
+
qc.setQueryData([name, id], (old) => ({
|
|
222
|
+
...old,
|
|
223
|
+
...data
|
|
224
|
+
}));
|
|
225
|
+
return { prev };
|
|
226
|
+
} : void 0,
|
|
227
|
+
onError: (error, variables, context) => {
|
|
228
|
+
var _a;
|
|
229
|
+
const ctx = context;
|
|
230
|
+
if (optimistic && (ctx == null ? void 0 : ctx.prev)) qc.setQueryData([name, variables.id], ctx.prev);
|
|
231
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onError) == null ? void 0 : _a.call(mutationOptions, error, variables);
|
|
232
|
+
},
|
|
233
|
+
onSuccess: (data, variables) => {
|
|
234
|
+
var _a;
|
|
235
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
236
|
+
qc.invalidateQueries({ queryKey: [name, variables.id] });
|
|
237
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, data, variables);
|
|
238
|
+
},
|
|
239
|
+
onSettled: mutationOptions == null ? void 0 : mutationOptions.onSettled
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function usePatch(mutationOptions) {
|
|
243
|
+
const qc = useQueryClient();
|
|
244
|
+
return useMutation({
|
|
245
|
+
mutationFn: async ({ id, data }) => {
|
|
246
|
+
const res = await client.patch(`${path}/${id}`, data);
|
|
247
|
+
return applyTransform(res.data);
|
|
248
|
+
},
|
|
249
|
+
onMutate: optimistic ? async ({ id, data }) => {
|
|
250
|
+
await qc.cancelQueries({ queryKey: [name, id] });
|
|
251
|
+
const prev = qc.getQueryData([name, id]);
|
|
252
|
+
qc.setQueryData([name, id], (old) => ({
|
|
253
|
+
...old,
|
|
254
|
+
...data
|
|
255
|
+
}));
|
|
256
|
+
return { prev };
|
|
257
|
+
} : void 0,
|
|
258
|
+
onError: (error, variables, context) => {
|
|
259
|
+
var _a;
|
|
260
|
+
const ctx = context;
|
|
261
|
+
if (optimistic && (ctx == null ? void 0 : ctx.prev)) qc.setQueryData([name, variables.id], ctx.prev);
|
|
262
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onError) == null ? void 0 : _a.call(mutationOptions, error, variables);
|
|
263
|
+
},
|
|
264
|
+
onSuccess: (data, variables) => {
|
|
265
|
+
var _a;
|
|
266
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
267
|
+
qc.invalidateQueries({ queryKey: [name, variables.id] });
|
|
268
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, data, variables);
|
|
269
|
+
},
|
|
270
|
+
onSettled: mutationOptions == null ? void 0 : mutationOptions.onSettled
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function useDelete(mutationOptions) {
|
|
274
|
+
const qc = useQueryClient();
|
|
275
|
+
return useMutation({
|
|
276
|
+
mutationFn: async (id) => {
|
|
277
|
+
await client.delete(`${path}/${id}`);
|
|
278
|
+
},
|
|
279
|
+
onMutate: optimistic ? async (id) => {
|
|
280
|
+
await qc.cancelQueries({ queryKey: [name] });
|
|
281
|
+
const prev = qc.getQueryData([name]);
|
|
282
|
+
qc.setQueryData(
|
|
283
|
+
[name],
|
|
284
|
+
(old = []) => old.filter((item) => item.id !== id)
|
|
285
|
+
);
|
|
286
|
+
return { prev };
|
|
287
|
+
} : void 0,
|
|
288
|
+
onError: (error, id, context) => {
|
|
289
|
+
var _a;
|
|
290
|
+
const ctx = context;
|
|
291
|
+
if (optimistic && (ctx == null ? void 0 : ctx.prev)) qc.setQueryData([name], ctx.prev);
|
|
292
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onError) == null ? void 0 : _a.call(mutationOptions, error, id);
|
|
293
|
+
},
|
|
294
|
+
onSuccess: (_, id) => {
|
|
295
|
+
var _a;
|
|
296
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
297
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, void 0, id);
|
|
298
|
+
},
|
|
299
|
+
onSettled: mutationOptions == null ? void 0 : mutationOptions.onSettled
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function useBulkDelete(mutationOptions) {
|
|
303
|
+
const qc = useQueryClient();
|
|
304
|
+
return useMutation({
|
|
305
|
+
mutationFn: async (ids) => {
|
|
306
|
+
await client.delete(path, { data: { ids } });
|
|
307
|
+
},
|
|
308
|
+
onSuccess: (_, ids) => {
|
|
309
|
+
var _a;
|
|
310
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
311
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, void 0, ids);
|
|
312
|
+
},
|
|
313
|
+
onError: mutationOptions == null ? void 0 : mutationOptions.onError
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function useBulkUpdate(mutationOptions) {
|
|
317
|
+
const qc = useQueryClient();
|
|
318
|
+
return useMutation({
|
|
319
|
+
mutationFn: async ({ ids, data }) => {
|
|
320
|
+
const res = await client.patch(path, { ids, ...data });
|
|
321
|
+
return applyTransform(res.data);
|
|
322
|
+
},
|
|
323
|
+
onSuccess: (data, variables) => {
|
|
324
|
+
var _a;
|
|
325
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
326
|
+
(_a = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a.call(mutationOptions, data, variables);
|
|
327
|
+
},
|
|
328
|
+
onError: mutationOptions == null ? void 0 : mutationOptions.onError
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function useUpload(uploadOptions) {
|
|
332
|
+
const qc = useQueryClient();
|
|
333
|
+
return useMutation({
|
|
334
|
+
mutationFn: async ({ id, file, extraFields }) => {
|
|
335
|
+
var _a;
|
|
336
|
+
const formData = new FormData();
|
|
337
|
+
const fieldName = (_a = uploadOptions == null ? void 0 : uploadOptions.fieldName) != null ? _a : "file";
|
|
338
|
+
if (Array.isArray(file)) {
|
|
339
|
+
file.forEach((f) => formData.append(fieldName, f));
|
|
340
|
+
} else {
|
|
341
|
+
formData.append(fieldName, file);
|
|
342
|
+
}
|
|
343
|
+
const mergedExtra = { ...uploadOptions == null ? void 0 : uploadOptions.extraFields, ...extraFields };
|
|
344
|
+
Object.entries(mergedExtra).forEach(([k, v]) => formData.append(k, v));
|
|
345
|
+
const url = id ? `${path}/${id}/upload` : `${path}/upload`;
|
|
346
|
+
const res = await client.post(url, formData, {
|
|
347
|
+
headers: { "Content-Type": "multipart/form-data" },
|
|
348
|
+
onUploadProgress: (e) => {
|
|
349
|
+
if ((uploadOptions == null ? void 0 : uploadOptions.onProgress) && e.total) {
|
|
350
|
+
uploadOptions.onProgress(Math.round(e.loaded * 100 / e.total));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
return applyTransform(res.data);
|
|
355
|
+
},
|
|
356
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: [name] })
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function useFormData(opts) {
|
|
360
|
+
var _a, _b;
|
|
361
|
+
const qc = useQueryClient();
|
|
362
|
+
const method = (_a = opts == null ? void 0 : opts.method) != null ? _a : "post";
|
|
363
|
+
const url = (opts == null ? void 0 : opts.id) ? `${path}/${opts.id}` : path;
|
|
364
|
+
return useMutation({
|
|
365
|
+
mutationFn: async (formData) => {
|
|
366
|
+
const res = await client[method](url, formData, {
|
|
367
|
+
headers: { "Content-Type": "multipart/form-data" },
|
|
368
|
+
onUploadProgress: (e) => {
|
|
369
|
+
if ((opts == null ? void 0 : opts.onProgress) && e.total) {
|
|
370
|
+
opts.onProgress(Math.round(e.loaded * 100 / e.total));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
return applyTransform(res.data);
|
|
375
|
+
},
|
|
376
|
+
onSuccess: (data, variables) => {
|
|
377
|
+
var _a2, _b2;
|
|
378
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
379
|
+
(_b2 = (_a2 = opts == null ? void 0 : opts.mutationOptions) == null ? void 0 : _a2.onSuccess) == null ? void 0 : _b2.call(_a2, data, variables);
|
|
380
|
+
},
|
|
381
|
+
onError: (_b = opts == null ? void 0 : opts.mutationOptions) == null ? void 0 : _b.onError
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
function useDownload(downloadOptions) {
|
|
385
|
+
return useMutation({
|
|
386
|
+
mutationFn: async ({ id, params }) => {
|
|
387
|
+
var _a, _b;
|
|
388
|
+
const url = id ? `${path}/${id}/download` : `${path}/download`;
|
|
389
|
+
const res = await client.get(url, {
|
|
390
|
+
params,
|
|
391
|
+
responseType: "blob",
|
|
392
|
+
onDownloadProgress: (e) => {
|
|
393
|
+
if ((downloadOptions == null ? void 0 : downloadOptions.onProgress) && e.total) {
|
|
394
|
+
downloadOptions.onProgress(Math.round(e.loaded * 100 / e.total));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
if ((downloadOptions == null ? void 0 : downloadOptions.saveAs) !== false) {
|
|
399
|
+
const filename = (_b = (_a = downloadOptions == null ? void 0 : downloadOptions.filename) != null ? _a : extractFilename(res.headers["content-disposition"])) != null ? _b : `${name}-download`;
|
|
400
|
+
triggerBrowserDownload(res.data, filename, downloadOptions == null ? void 0 : downloadOptions.mimeType);
|
|
401
|
+
}
|
|
402
|
+
return res.data;
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function useExport(opts) {
|
|
407
|
+
return useMutation({
|
|
408
|
+
mutationFn: async (queryOptions) => {
|
|
409
|
+
var _a, _b, _c;
|
|
410
|
+
const params = buildQueryParams(queryOptions, defaultParams);
|
|
411
|
+
const res = await client.get(`${path}/export`, {
|
|
412
|
+
params: { ...params, format: (_a = opts == null ? void 0 : opts.format) != null ? _a : "csv" },
|
|
413
|
+
responseType: "blob",
|
|
414
|
+
onDownloadProgress: (e) => {
|
|
415
|
+
if ((opts == null ? void 0 : opts.onProgress) && e.total) {
|
|
416
|
+
opts.onProgress(Math.round(e.loaded * 100 / e.total));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
const fmt = (_b = opts == null ? void 0 : opts.format) != null ? _b : "csv";
|
|
421
|
+
const filename = (_c = opts == null ? void 0 : opts.filename) != null ? _c : `${name}-export.${fmt}`;
|
|
422
|
+
const mimeMap = {
|
|
423
|
+
csv: "text/csv",
|
|
424
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
425
|
+
pdf: "application/pdf"
|
|
426
|
+
};
|
|
427
|
+
triggerBrowserDownload(res.data, filename, mimeMap[fmt]);
|
|
428
|
+
return res.data;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function useAction(action, opts) {
|
|
433
|
+
var _a, _b;
|
|
434
|
+
const qc = useQueryClient();
|
|
435
|
+
const method = (_a = opts == null ? void 0 : opts.method) != null ? _a : "post";
|
|
436
|
+
const url = (opts == null ? void 0 : opts.id) ? `${path}/${opts.id}/${action}` : `${path}/${action}`;
|
|
437
|
+
return useMutation({
|
|
438
|
+
mutationFn: async (payload) => {
|
|
439
|
+
const res = await client[method](url, payload);
|
|
440
|
+
return applyTransform(res.data);
|
|
441
|
+
},
|
|
442
|
+
onSuccess: (data, variables) => {
|
|
443
|
+
var _a2, _b2;
|
|
444
|
+
if ((opts == null ? void 0 : opts.invalidate) !== false) {
|
|
445
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
446
|
+
}
|
|
447
|
+
(_b2 = (_a2 = opts == null ? void 0 : opts.mutationOptions) == null ? void 0 : _a2.onSuccess) == null ? void 0 : _b2.call(_a2, data, variables);
|
|
448
|
+
},
|
|
449
|
+
onError: (_b = opts == null ? void 0 : opts.mutationOptions) == null ? void 0 : _b.onError
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function useInvalidate() {
|
|
453
|
+
const qc = useQueryClient();
|
|
454
|
+
return (id) => {
|
|
455
|
+
if (id) {
|
|
456
|
+
qc.invalidateQueries({ queryKey: [name, id] });
|
|
457
|
+
} else {
|
|
458
|
+
qc.invalidateQueries({ queryKey: [name] });
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function usePrefetch() {
|
|
463
|
+
const qc = useQueryClient();
|
|
464
|
+
return (id) => {
|
|
465
|
+
if (id) {
|
|
466
|
+
qc.prefetchQuery({
|
|
467
|
+
queryKey: [name, id],
|
|
468
|
+
queryFn: () => client.get(`${path}/${id}`).then((r) => r.data)
|
|
469
|
+
});
|
|
470
|
+
} else {
|
|
471
|
+
qc.prefetchQuery({
|
|
472
|
+
queryKey: [name],
|
|
473
|
+
queryFn: () => client.get(path).then((r) => r.data)
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function useSetCache() {
|
|
479
|
+
const qc = useQueryClient();
|
|
480
|
+
return (data, id) => {
|
|
481
|
+
if (id) {
|
|
482
|
+
qc.setQueryData([name, id], data);
|
|
483
|
+
} else {
|
|
484
|
+
qc.setQueryData([name], data);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
// ── Queries ──────────────────────────────────────────────────────────
|
|
490
|
+
/** Full list — with filters, search, sorting, polling */
|
|
491
|
+
useList,
|
|
492
|
+
/** Single item by ID */
|
|
493
|
+
useGet,
|
|
494
|
+
/** Page-number pagination */
|
|
495
|
+
usePaginatedList,
|
|
496
|
+
/** Offset-based infinite scroll */
|
|
497
|
+
useInfiniteList,
|
|
498
|
+
/** Cursor-based infinite scroll (for DB cursor pagination) */
|
|
499
|
+
useCursorList,
|
|
500
|
+
// ── Mutations ─────────────────────────────────────────────────────────
|
|
501
|
+
/** Create — POST /resource */
|
|
502
|
+
useCreate,
|
|
503
|
+
/** Full replace — PUT /resource/:id */
|
|
504
|
+
useUpdate,
|
|
505
|
+
/** Partial update — PATCH /resource/:id */
|
|
506
|
+
usePatch,
|
|
507
|
+
/** Delete — DELETE /resource/:id */
|
|
508
|
+
useDelete,
|
|
509
|
+
/** Delete many — DELETE /resource { ids: [...] } */
|
|
510
|
+
useBulkDelete,
|
|
511
|
+
/** Update many — PATCH /resource { ids: [...], ...fields } */
|
|
512
|
+
useBulkUpdate,
|
|
513
|
+
// ── File Operations ───────────────────────────────────────────────────
|
|
514
|
+
/** Upload file(s) with progress tracking */
|
|
515
|
+
useUpload,
|
|
516
|
+
/** Submit any FormData (mixed files + fields) */
|
|
517
|
+
useFormData,
|
|
518
|
+
/** Download blob file with progress + auto "Save As" */
|
|
519
|
+
useDownload,
|
|
520
|
+
/** Export list to CSV / XLSX / PDF */
|
|
521
|
+
useExport,
|
|
522
|
+
// ── Custom Actions ────────────────────────────────────────────────────
|
|
523
|
+
/** Arbitrary endpoint action (e.g. /resource/:id/approve) */
|
|
524
|
+
useAction,
|
|
525
|
+
// ── Cache Utilities ───────────────────────────────────────────────────
|
|
526
|
+
/** Manually invalidate cache */
|
|
527
|
+
useInvalidate,
|
|
528
|
+
/** Prefetch on hover/navigation */
|
|
529
|
+
usePrefetch,
|
|
530
|
+
/** Manually write data into cache (SSR/seed) */
|
|
531
|
+
useSetCache,
|
|
532
|
+
// ── Meta ──────────────────────────────────────────────────────────────
|
|
533
|
+
path,
|
|
534
|
+
name
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function extractFilename(contentDisposition) {
|
|
538
|
+
if (!contentDisposition) return null;
|
|
539
|
+
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
|
540
|
+
return match ? match[1].replace(/['"]/g, "").trim() : null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/createApiClient.ts
|
|
544
|
+
function createApiClient(config) {
|
|
545
|
+
const {
|
|
546
|
+
baseURL,
|
|
547
|
+
getToken,
|
|
548
|
+
onTokenRefresh,
|
|
549
|
+
onAuthFailure,
|
|
550
|
+
onError,
|
|
551
|
+
headers = {},
|
|
552
|
+
timeout = 3e4,
|
|
553
|
+
retry = { count: 0, delay: 1e3, statusCodes: [429, 503] }
|
|
554
|
+
} = config;
|
|
555
|
+
const client = axios.create({
|
|
556
|
+
baseURL,
|
|
557
|
+
timeout,
|
|
558
|
+
headers: {
|
|
559
|
+
"Content-Type": "application/json",
|
|
560
|
+
...headers
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
client.interceptors.request.use(async (req) => {
|
|
564
|
+
const token = await (getToken == null ? void 0 : getToken());
|
|
565
|
+
if (token) {
|
|
566
|
+
req.headers.Authorization = `Bearer ${token}`;
|
|
567
|
+
}
|
|
568
|
+
return req;
|
|
569
|
+
});
|
|
570
|
+
let isRefreshing = false;
|
|
571
|
+
let refreshQueue = [];
|
|
572
|
+
function processRefreshQueue(token, error = null) {
|
|
573
|
+
refreshQueue.forEach(({ resolve, reject }) => {
|
|
574
|
+
if (token) resolve(token);
|
|
575
|
+
else reject(error);
|
|
576
|
+
});
|
|
577
|
+
refreshQueue = [];
|
|
578
|
+
}
|
|
579
|
+
client.interceptors.response.use(
|
|
580
|
+
(res) => res,
|
|
581
|
+
async (error) => {
|
|
582
|
+
var _a, _b;
|
|
583
|
+
const originalRequest = error.config;
|
|
584
|
+
if (onError) onError(error);
|
|
585
|
+
if (((_a = error.response) == null ? void 0 : _a.status) === 401 && originalRequest && !originalRequest._retryCount) {
|
|
586
|
+
if (isRefreshing) {
|
|
587
|
+
return new Promise((resolve, reject) => {
|
|
588
|
+
refreshQueue.push({ resolve, reject });
|
|
589
|
+
}).then((token) => {
|
|
590
|
+
originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
591
|
+
return client(originalRequest);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
if (onTokenRefresh) {
|
|
595
|
+
isRefreshing = true;
|
|
596
|
+
try {
|
|
597
|
+
const newToken = await refreshTokenRequest(config);
|
|
598
|
+
onTokenRefresh(newToken);
|
|
599
|
+
processRefreshQueue(newToken);
|
|
600
|
+
isRefreshing = false;
|
|
601
|
+
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
602
|
+
return client(originalRequest);
|
|
603
|
+
} catch (refreshError) {
|
|
604
|
+
processRefreshQueue(null, refreshError);
|
|
605
|
+
isRefreshing = false;
|
|
606
|
+
onAuthFailure == null ? void 0 : onAuthFailure();
|
|
607
|
+
return Promise.reject(refreshError);
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
onAuthFailure == null ? void 0 : onAuthFailure();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (originalRequest && shouldRetry(error, retry)) {
|
|
614
|
+
originalRequest._retryCount = ((_b = originalRequest._retryCount) != null ? _b : 0) + 1;
|
|
615
|
+
if (originalRequest._retryCount <= retry.count) {
|
|
616
|
+
const delay = retry.delay !== void 0 ? retry.delay * originalRequest._retryCount : 1e3;
|
|
617
|
+
await sleep(delay);
|
|
618
|
+
return client(originalRequest);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return Promise.reject(error);
|
|
622
|
+
}
|
|
623
|
+
);
|
|
624
|
+
return {
|
|
625
|
+
/** Access the raw axios instance for custom requests */
|
|
626
|
+
axiosInstance: client,
|
|
627
|
+
/** Create a resource with CRUD hooks */
|
|
628
|
+
resource: (name, options) => createResource(client, name, options),
|
|
629
|
+
/** Raw GET */
|
|
630
|
+
get: (url, axiosConfig) => client.get(url, axiosConfig).then((r) => r.data),
|
|
631
|
+
/** Raw POST */
|
|
632
|
+
post: (url, data, axiosConfig) => client.post(url, data, axiosConfig).then((r) => r.data),
|
|
633
|
+
/** Raw PUT */
|
|
634
|
+
put: (url, data, axiosConfig) => client.put(url, data, axiosConfig).then((r) => r.data),
|
|
635
|
+
/** Raw PATCH */
|
|
636
|
+
patch: (url, data, axiosConfig) => client.patch(url, data, axiosConfig).then((r) => r.data),
|
|
637
|
+
/** Raw DELETE */
|
|
638
|
+
delete: (url, axiosConfig) => client.delete(url, axiosConfig).then((r) => r.data)
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
async function refreshTokenRequest(config) {
|
|
642
|
+
var _a, _b, _c, _d;
|
|
643
|
+
const currentToken = await ((_a = config.getToken) == null ? void 0 : _a.call(config));
|
|
644
|
+
const res = await axios.post(
|
|
645
|
+
`${config.baseURL}/auth/refresh`,
|
|
646
|
+
{},
|
|
647
|
+
{
|
|
648
|
+
headers: currentToken ? { Authorization: `Bearer ${currentToken}` } : void 0
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
return (_d = (_b = res.data) == null ? void 0 : _b.token) != null ? _d : (_c = res.data) == null ? void 0 : _c.accessToken;
|
|
652
|
+
}
|
|
653
|
+
function shouldRetry(error, retry) {
|
|
654
|
+
if (!retry.count) return false;
|
|
655
|
+
if (!error.response) return true;
|
|
656
|
+
if (retry.statusCodes && retry.statusCodes.length > 0) {
|
|
657
|
+
return retry.statusCodes.includes(error.response.status);
|
|
658
|
+
}
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
export {
|
|
662
|
+
createApiClient,
|
|
663
|
+
createResource
|
|
664
|
+
};
|