@ventlio/tanstack-query 0.2.63 → 0.2.65

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.
Files changed (36) hide show
  1. package/dist/config/bootstrapQueryRequest.d.ts +2 -2
  2. package/dist/config/bootstrapQueryRequest.js +1 -1
  3. package/dist/config/useEnvironmentVariables.js +2 -2
  4. package/dist/config/useReactNativeEnv.d.ts +1 -0
  5. package/dist/config/useReactNativeEnv.js +5 -16
  6. package/dist/config/useReactNativeEnv.js.map +1 -1
  7. package/dist/index.mjs +157 -29
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/model/index.d.ts +1 -0
  10. package/dist/model/model.interface.d.ts +9 -0
  11. package/dist/model/useQueryModel.d.ts +2 -2
  12. package/dist/model/useQueryModel.js +102 -2
  13. package/dist/model/useQueryModel.js.map +1 -1
  14. package/dist/queries/useDeleteRequest.js +1 -1
  15. package/dist/queries/usePatchRequest.js +0 -1
  16. package/dist/queries/usePatchRequest.js.map +1 -1
  17. package/dist/queries/usePostRequest.d.ts +2 -1
  18. package/dist/queries/usePostRequest.js +7 -2
  19. package/dist/queries/usePostRequest.js.map +1 -1
  20. package/dist/request/make-request.d.ts +1 -1
  21. package/dist/request/make-request.js +41 -7
  22. package/dist/request/make-request.js.map +1 -1
  23. package/dist/request/request.interface.d.ts +5 -0
  24. package/dist/types/index.d.ts +8 -3
  25. package/package.json +8 -2
  26. package/src/config/bootstrapQueryRequest.ts +3 -3
  27. package/src/config/useEnvironmentVariables.ts +2 -2
  28. package/src/config/useReactNativeEnv.ts +5 -20
  29. package/src/model/index.ts +1 -0
  30. package/src/model/model.interface.ts +10 -0
  31. package/src/model/useQueryModel.ts +123 -3
  32. package/src/queries/useDeleteRequest.ts +1 -1
  33. package/src/queries/usePostRequest.ts +8 -2
  34. package/src/request/make-request.ts +42 -6
  35. package/src/request/request.interface.ts +5 -0
  36. package/src/types/index.ts +9 -3
@@ -1,3 +1,3 @@
1
1
  import type { QueryClient } from '@tanstack/react-query';
2
- import type { BootstrapQueryRequest } from '../types';
3
- export declare const bootstrapQueryRequest: (queryClient: QueryClient, options?: BootstrapQueryRequest) => void;
2
+ import type { BootstrapConfig } from '../types';
3
+ export declare const bootstrapQueryRequest: (queryClient: QueryClient, options?: BootstrapConfig) => void;
@@ -4,7 +4,7 @@ const bootstrapQueryRequest = (queryClient, options) => {
4
4
  staleTime: Infinity,
5
5
  cacheTime: Infinity,
6
6
  });
7
- // set default query confg
7
+ // set default query config
8
8
  queryClient.setQueryData(['config'], {
9
9
  headers: {
10
10
  Authorization: ``,
@@ -2,8 +2,8 @@ import { useReactNativeEnv } from './useReactNativeEnv.js';
2
2
 
3
3
  const useEnvironmentVariables = () => {
4
4
  const { appTimeout, appUrl } = useReactNativeEnv();
5
- const url = process.env.REACT_APP_API_URL || process.env.NEXT_PUBLIC_API_URL || appUrl;
6
- const timeout = process.env.REACT_APP_API_TIMEOUT || process.env.NEXT_PUBLIC_API_TIMEOUT || appTimeout;
5
+ const url = process.env.REACT_APP_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? appUrl;
6
+ const timeout = process.env.REACT_APP_API_TIMEOUT ?? process.env.NEXT_PUBLIC_API_TIMEOUT ?? appTimeout;
7
7
  return {
8
8
  API_URL: url,
9
9
  TIMEOUT: Number(timeout),
@@ -1,4 +1,5 @@
1
1
  export declare const useReactNativeEnv: () => {
2
2
  appUrl: string | undefined;
3
3
  appTimeout: number | undefined;
4
+ isApp: boolean;
4
5
  };
@@ -1,23 +1,12 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
- import { useState, useEffect } from 'react';
3
2
 
4
3
  const useReactNativeEnv = () => {
5
4
  const queryClient = useQueryClient();
6
- const [appUrl, setAppUrl] = useState(undefined);
7
- const [appTimeout, setAppTimeout] = useState();
8
- useEffect(() => {
9
- const config = queryClient.getQueryData(['config']);
10
- const loadReactNativeEnvIfNeeded = async () => {
11
- if (config?.options?.context === 'app') {
12
- const API_URL = config.options.environments?.appBaseUrl;
13
- const API_TIMEOUT = config.options.environments?.appTimeout;
14
- setAppUrl(API_URL);
15
- setAppTimeout(API_TIMEOUT);
16
- }
17
- };
18
- loadReactNativeEnvIfNeeded();
19
- }, [queryClient]);
20
- return { appUrl, appTimeout };
5
+ const config = queryClient.getQueryData(['config']);
6
+ const appUrl = config?.options?.environments?.appBaseUrl;
7
+ const appTimeout = config?.options?.environments?.appTimeout;
8
+ const isApp = config?.options?.context === 'app';
9
+ return { appUrl, appTimeout, isApp };
21
10
  };
22
11
 
23
12
  export { useReactNativeEnv };
@@ -1 +1 @@
1
- {"version":3,"file":"useReactNativeEnv.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useReactNativeEnv.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
2
- import { useState, useEffect, useMemo, startTransition } from 'react';
2
+ import result from 'lodash.result';
3
+ import set from 'lodash.set';
4
+ import { useState, useMemo, useEffect, startTransition } from 'react';
3
5
  import axios from 'axios';
4
6
 
5
7
  const bootstrapQueryRequest = (queryClient, options) => {
@@ -8,7 +10,7 @@ const bootstrapQueryRequest = (queryClient, options) => {
8
10
  staleTime: Infinity,
9
11
  cacheTime: Infinity,
10
12
  });
11
- // set default query confg
13
+ // set default query config
12
14
  queryClient.setQueryData(['config'], {
13
15
  headers: {
14
16
  Authorization: ``,
@@ -19,27 +21,17 @@ const bootstrapQueryRequest = (queryClient, options) => {
19
21
 
20
22
  const useReactNativeEnv = () => {
21
23
  const queryClient = useQueryClient();
22
- const [appUrl, setAppUrl] = useState(undefined);
23
- const [appTimeout, setAppTimeout] = useState();
24
- useEffect(() => {
25
- const config = queryClient.getQueryData(['config']);
26
- const loadReactNativeEnvIfNeeded = async () => {
27
- if (config?.options?.context === 'app') {
28
- const API_URL = config.options.environments?.appBaseUrl;
29
- const API_TIMEOUT = config.options.environments?.appTimeout;
30
- setAppUrl(API_URL);
31
- setAppTimeout(API_TIMEOUT);
32
- }
33
- };
34
- loadReactNativeEnvIfNeeded();
35
- }, [queryClient]);
36
- return { appUrl, appTimeout };
24
+ const config = queryClient.getQueryData(['config']);
25
+ const appUrl = config?.options?.environments?.appBaseUrl;
26
+ const appTimeout = config?.options?.environments?.appTimeout;
27
+ const isApp = config?.options?.context === 'app';
28
+ return { appUrl, appTimeout, isApp };
37
29
  };
38
30
 
39
31
  const useEnvironmentVariables = () => {
40
32
  const { appTimeout, appUrl } = useReactNativeEnv();
41
- const url = process.env.REACT_APP_API_URL || process.env.NEXT_PUBLIC_API_URL || appUrl;
42
- const timeout = process.env.REACT_APP_API_TIMEOUT || process.env.NEXT_PUBLIC_API_TIMEOUT || appTimeout;
33
+ const url = process.env.REACT_APP_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? appUrl;
34
+ const timeout = process.env.REACT_APP_API_TIMEOUT ?? process.env.NEXT_PUBLIC_API_TIMEOUT ?? appTimeout;
43
35
  return {
44
36
  API_URL: url,
45
37
  TIMEOUT: Number(timeout),
@@ -107,9 +99,106 @@ const useKeyTrackerModel = (keyTracker) => {
107
99
  return { refetchQuery, getQueryKey };
108
100
  };
109
101
 
110
- const useQueryModel = (queryKey, filters) => {
102
+ const useQueryModel = (keyTracker, exact = true) => {
111
103
  const queryClient = useQueryClient();
112
- return queryClient.getQueryData(queryKey, filters);
104
+ const { getQueryKey } = useKeyTrackerModel(keyTracker);
105
+ const queryKey = getQueryKey();
106
+ const add = (data, position, path) => {
107
+ let records = findAll(path) ?? [];
108
+ if (!position || position === 'end') {
109
+ records = [...records, data];
110
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
111
+ }
112
+ else if (position === 'start') {
113
+ records = [data, ...records];
114
+ }
115
+ if (!path) {
116
+ queryClient.setQueryData(queryKey, records);
117
+ }
118
+ else {
119
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
120
+ queryClient.setQueryData(queryKey, set(queryData, path, records));
121
+ }
122
+ return data;
123
+ };
124
+ const findAll = (path) => {
125
+ const data = queryClient.getQueryData(queryKey, { exact });
126
+ if (!data) {
127
+ return [];
128
+ }
129
+ if (!path) {
130
+ return Array.isArray(data) ? data : [data];
131
+ }
132
+ return result(data, path, []);
133
+ };
134
+ const findMany = (selector, path) => {
135
+ const data = findAll(path) ?? [];
136
+ return data.filter(selector);
137
+ };
138
+ const find = (id, path) => {
139
+ const modelConfig = getModelConfig();
140
+ if (!modelConfig?.idColumn) {
141
+ return undefined;
142
+ }
143
+ const data = findAll(path) ?? [];
144
+ return data.find((record) => record[modelConfig.idColumn] === id);
145
+ };
146
+ const getModelConfig = () => {
147
+ const { options } = queryClient.getQueryData(['config']) ?? {};
148
+ const { modelConfig } = options ?? {};
149
+ return modelConfig;
150
+ };
151
+ const update = (id, data, path) => {
152
+ const oldData = findAll(path) ?? [];
153
+ const modelConfig = getModelConfig();
154
+ if (!modelConfig?.idColumn) {
155
+ return undefined;
156
+ }
157
+ const idColumn = modelConfig.idColumn;
158
+ let updatedRecord = undefined;
159
+ const newData = oldData.map((record) => {
160
+ let dataRecord = record;
161
+ if (dataRecord[idColumn] === id) {
162
+ dataRecord = { ...dataRecord, ...data };
163
+ updatedRecord = dataRecord;
164
+ }
165
+ return dataRecord;
166
+ });
167
+ if (!path) {
168
+ queryClient.setQueryData(queryKey, newData);
169
+ }
170
+ else {
171
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
172
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
173
+ }
174
+ return updatedRecord;
175
+ };
176
+ const remove = (id, path) => {
177
+ const oldData = findAll(path) ?? [];
178
+ const modelConfig = getModelConfig();
179
+ if (!modelConfig?.idColumn) {
180
+ return false;
181
+ }
182
+ const idColumn = modelConfig.idColumn;
183
+ let updated = false;
184
+ const newData = oldData.filter((record) => {
185
+ const dataRecord = record;
186
+ if (dataRecord[idColumn] === id) {
187
+ updated = true;
188
+ return false;
189
+ }
190
+ return true;
191
+ });
192
+ if (!path) {
193
+ queryClient.setQueryData(queryKey, newData);
194
+ }
195
+ else {
196
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
197
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
198
+ }
199
+ return updated;
200
+ };
201
+ return { find, findAll, findMany, remove, update, add };
113
202
  };
114
203
 
115
204
  const useRefetchQuery = async (queryKey) => {
@@ -209,20 +298,41 @@ const successTransformer = (data) => {
209
298
  };
210
299
  };
211
300
 
212
- async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, }) {
301
+ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, appFileConfig, }) {
302
+ // check if file is included in mobile app environment and extract all file input to avoid
303
+ // it being formatted to object using axios formData builder
304
+ const isApp = appFileConfig?.isApp;
305
+ const appFiles = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
213
306
  // configure body
214
- body = isFormData ? buildFormData(body) : body;
215
- // configure request header
307
+ body = (isFormData ? axios.toFormData(body) : body);
308
+ // configure request header1
216
309
  if (!isFormData) {
217
310
  headers['Content-Type'] = ContentType.APPLICATION_JSON;
218
311
  }
219
312
  else {
220
- delete headers['Content-Type'];
313
+ if (isApp) {
314
+ headers['Content-Type'] = ContentType.MULTIPART_FORM_DATA;
315
+ // add the app files
316
+ for (const fileKey in appFiles) {
317
+ const currentFile = appFiles[fileKey];
318
+ if (Array.isArray(currentFile)) {
319
+ for (const innerFile of currentFile) {
320
+ body.append(fileKey, innerFile);
321
+ }
322
+ }
323
+ else {
324
+ body.append(fileKey, currentFile);
325
+ }
326
+ }
327
+ }
328
+ else {
329
+ delete headers['Content-Type'];
330
+ }
221
331
  }
222
332
  try {
223
- const axios = axiosInstance({ baseURL, headers, timeout });
333
+ const axiosRequest = axiosInstance({ baseURL, headers, timeout });
224
334
  // send request
225
- const resp = await axios({
335
+ const resp = await axiosRequest({
226
336
  url: path,
227
337
  method,
228
338
  data: body,
@@ -250,6 +360,19 @@ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, he
250
360
  ...errorData,
251
361
  });
252
362
  }
363
+ }
364
+ function getAppFiles(body, fileSelectors = []) {
365
+ const files = {};
366
+ if (body) {
367
+ if (fileSelectors.length > 0) {
368
+ //
369
+ for (const fileKey of fileSelectors) {
370
+ files[fileKey] = body[fileKey];
371
+ delete body[fileKey];
372
+ }
373
+ }
374
+ }
375
+ return files;
253
376
  }
254
377
 
255
378
  const useDeleteRequest = (deleteOptions) => {
@@ -287,7 +410,7 @@ const useDeleteRequest = (deleteOptions) => {
287
410
  internalDeleteOptions = internalDeleteOptions ?? {};
288
411
  internalDeleteOptions.enabled = true;
289
412
  await updatedPathAsync(link);
290
- await setOptionsAsync(deleteOptions);
413
+ await setOptionsAsync(internalDeleteOptions);
291
414
  return query.data;
292
415
  };
293
416
  return { destroy, ...query };
@@ -430,10 +553,11 @@ const usePatchRequest = ({ path, baseUrl, headers }) => {
430
553
  return { patch, ...mutation };
431
554
  };
432
555
 
433
- const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
556
+ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelectors, }) => {
434
557
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
435
558
  const queryClient = useQueryClient();
436
559
  const { getHeaders } = useQueryHeaders();
560
+ const { isApp } = useReactNativeEnv();
437
561
  const sendRequest = async (res, rej, postData) => {
438
562
  // get request headers
439
563
  const globalHeaders = getHeaders();
@@ -446,6 +570,10 @@ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
446
570
  headers: { ...globalHeaders, ...headers },
447
571
  baseURL: baseUrl ?? API_URL,
448
572
  timeout: TIMEOUT,
573
+ appFileConfig: {
574
+ isApp,
575
+ fileSelectors,
576
+ },
449
577
  });
450
578
  if (postResponse.status) {
451
579
  // scroll to top after success
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,3 +1,4 @@
1
+ export * from './model.interface';
1
2
  export * from './useKeyTrackerModel';
2
3
  export * from './useQueryModel';
3
4
  export * from './useRefetchQuery';
@@ -0,0 +1,9 @@
1
+ export interface QueryModelBuilder<T> {
2
+ add: (data: T, position?: QueryModelAddPosition, path?: string) => T | undefined;
3
+ findAll: (path?: string) => T[] | undefined;
4
+ findMany: (selector: (record: T) => boolean, path?: string) => T[];
5
+ find: (id: number | string, path?: string) => T | undefined;
6
+ update: (id: number | string, data: Partial<T>, path?: string) => T | undefined;
7
+ remove: (id: number, path?: string) => boolean;
8
+ }
9
+ export type QueryModelAddPosition = 'start' | 'end';
@@ -1,2 +1,2 @@
1
- import type { QueryFilters } from '@tanstack/react-query';
2
- export declare const useQueryModel: (queryKey: any[], filters?: QueryFilters | undefined) => unknown;
1
+ import type { QueryModelBuilder } from './model.interface';
2
+ export declare const useQueryModel: <T>(keyTracker: string, exact?: boolean) => QueryModelBuilder<T>;
@@ -1,8 +1,108 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
+ import result from 'lodash.result';
3
+ import set from 'lodash.set';
4
+ import { useKeyTrackerModel } from './useKeyTrackerModel.js';
2
5
 
3
- const useQueryModel = (queryKey, filters) => {
6
+ const useQueryModel = (keyTracker, exact = true) => {
4
7
  const queryClient = useQueryClient();
5
- return queryClient.getQueryData(queryKey, filters);
8
+ const { getQueryKey } = useKeyTrackerModel(keyTracker);
9
+ const queryKey = getQueryKey();
10
+ const add = (data, position, path) => {
11
+ let records = findAll(path) ?? [];
12
+ if (!position || position === 'end') {
13
+ records = [...records, data];
14
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
15
+ }
16
+ else if (position === 'start') {
17
+ records = [data, ...records];
18
+ }
19
+ if (!path) {
20
+ queryClient.setQueryData(queryKey, records);
21
+ }
22
+ else {
23
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
24
+ queryClient.setQueryData(queryKey, set(queryData, path, records));
25
+ }
26
+ return data;
27
+ };
28
+ const findAll = (path) => {
29
+ const data = queryClient.getQueryData(queryKey, { exact });
30
+ if (!data) {
31
+ return [];
32
+ }
33
+ if (!path) {
34
+ return Array.isArray(data) ? data : [data];
35
+ }
36
+ return result(data, path, []);
37
+ };
38
+ const findMany = (selector, path) => {
39
+ const data = findAll(path) ?? [];
40
+ return data.filter(selector);
41
+ };
42
+ const find = (id, path) => {
43
+ const modelConfig = getModelConfig();
44
+ if (!modelConfig?.idColumn) {
45
+ return undefined;
46
+ }
47
+ const data = findAll(path) ?? [];
48
+ return data.find((record) => record[modelConfig.idColumn] === id);
49
+ };
50
+ const getModelConfig = () => {
51
+ const { options } = queryClient.getQueryData(['config']) ?? {};
52
+ const { modelConfig } = options ?? {};
53
+ return modelConfig;
54
+ };
55
+ const update = (id, data, path) => {
56
+ const oldData = findAll(path) ?? [];
57
+ const modelConfig = getModelConfig();
58
+ if (!modelConfig?.idColumn) {
59
+ return undefined;
60
+ }
61
+ const idColumn = modelConfig.idColumn;
62
+ let updatedRecord = undefined;
63
+ const newData = oldData.map((record) => {
64
+ let dataRecord = record;
65
+ if (dataRecord[idColumn] === id) {
66
+ dataRecord = { ...dataRecord, ...data };
67
+ updatedRecord = dataRecord;
68
+ }
69
+ return dataRecord;
70
+ });
71
+ if (!path) {
72
+ queryClient.setQueryData(queryKey, newData);
73
+ }
74
+ else {
75
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
76
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
77
+ }
78
+ return updatedRecord;
79
+ };
80
+ const remove = (id, path) => {
81
+ const oldData = findAll(path) ?? [];
82
+ const modelConfig = getModelConfig();
83
+ if (!modelConfig?.idColumn) {
84
+ return false;
85
+ }
86
+ const idColumn = modelConfig.idColumn;
87
+ let updated = false;
88
+ const newData = oldData.filter((record) => {
89
+ const dataRecord = record;
90
+ if (dataRecord[idColumn] === id) {
91
+ updated = true;
92
+ return false;
93
+ }
94
+ return true;
95
+ });
96
+ if (!path) {
97
+ queryClient.setQueryData(queryKey, newData);
98
+ }
99
+ else {
100
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
101
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
102
+ }
103
+ return updated;
104
+ };
105
+ return { find, findAll, findMany, remove, update, add };
6
106
  };
7
107
 
8
108
  export { useQueryModel };
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryModel.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"useQueryModel.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -41,7 +41,7 @@ const useDeleteRequest = (deleteOptions) => {
41
41
  internalDeleteOptions = internalDeleteOptions ?? {};
42
42
  internalDeleteOptions.enabled = true;
43
43
  await updatedPathAsync(link);
44
- await setOptionsAsync(deleteOptions);
44
+ await setOptionsAsync(internalDeleteOptions);
45
45
  return query.data;
46
46
  };
47
47
  return { destroy, ...query };
@@ -1,7 +1,6 @@
1
1
  import { useMutation } from '@tanstack/react-query';
2
2
  import { useEnvironmentVariables } from '../config/useEnvironmentVariables.js';
3
3
  import { useQueryHeaders } from '../config/useQueryHeaders.js';
4
- import 'react';
5
4
  import { scrollToTop } from '../helpers/scrollToTop.js';
6
5
  import 'axios';
7
6
  import { makeRequest } from '../request/make-request.js';
@@ -1 +1 @@
1
- {"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,9 +1,10 @@
1
1
  import type { MutateOptions } from '@tanstack/react-query';
2
2
  import type { IRequestError, IRequestSuccess } from '../request';
3
3
  import type { DefaultRequestOptions } from './queries.interface';
4
- export declare const usePostRequest: <TResponse>({ path, isFormData, baseUrl, headers, }: {
4
+ export declare const usePostRequest: <TResponse>({ path, isFormData, baseUrl, headers, fileSelectors, }: {
5
5
  path: string;
6
6
  isFormData?: boolean | undefined;
7
+ fileSelectors?: string[] | undefined;
7
8
  } & DefaultRequestOptions) => {
8
9
  data: undefined;
9
10
  error: null;
@@ -1,16 +1,17 @@
1
1
  import { useQueryClient, useMutation } from '@tanstack/react-query';
2
2
  import { useEnvironmentVariables } from '../config/useEnvironmentVariables.js';
3
3
  import { useQueryHeaders } from '../config/useQueryHeaders.js';
4
- import 'react';
4
+ import { useReactNativeEnv } from '../config/useReactNativeEnv.js';
5
5
  import { scrollToTop } from '../helpers/scrollToTop.js';
6
6
  import 'axios';
7
7
  import { makeRequest } from '../request/make-request.js';
8
8
  import { HttpMethod } from '../request/request.enum.js';
9
9
 
10
- const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
10
+ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelectors, }) => {
11
11
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
12
12
  const queryClient = useQueryClient();
13
13
  const { getHeaders } = useQueryHeaders();
14
+ const { isApp } = useReactNativeEnv();
14
15
  const sendRequest = async (res, rej, postData) => {
15
16
  // get request headers
16
17
  const globalHeaders = getHeaders();
@@ -23,6 +24,10 @@ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
23
24
  headers: { ...globalHeaders, ...headers },
24
25
  baseURL: baseUrl ?? API_URL,
25
26
  timeout: TIMEOUT,
27
+ appFileConfig: {
28
+ isApp,
29
+ fileSelectors,
30
+ },
26
31
  });
27
32
  if (postResponse.status) {
28
33
  // scroll to top after success
@@ -1 +1 @@
1
- {"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,2 +1,2 @@
1
1
  import type { IMakeRequest } from './request.interface';
2
- export declare function makeRequest<TResponse>({ body, method, path, isFormData, headers, baseURL, timeout, }: IMakeRequest): Promise<import("./request.interface").IRequestError>;
2
+ export declare function makeRequest<TResponse>({ body, method, path, isFormData, headers, baseURL, timeout, appFileConfig, }: IMakeRequest): Promise<import("./request.interface").IRequestError>;
@@ -1,22 +1,43 @@
1
+ import axios from 'axios';
1
2
  import { axiosInstance } from './axios-instance.js';
2
- import { buildFormData } from './buildFormData.js';
3
3
  import { ContentType, HttpMethod } from './request.enum.js';
4
4
  import { errorTransformer, successTransformer } from './transformer.js';
5
5
 
6
- async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, }) {
6
+ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, appFileConfig, }) {
7
+ // check if file is included in mobile app environment and extract all file input to avoid
8
+ // it being formatted to object using axios formData builder
9
+ const isApp = appFileConfig?.isApp;
10
+ const appFiles = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
7
11
  // configure body
8
- body = isFormData ? buildFormData(body) : body;
9
- // configure request header
12
+ body = (isFormData ? axios.toFormData(body) : body);
13
+ // configure request header1
10
14
  if (!isFormData) {
11
15
  headers['Content-Type'] = ContentType.APPLICATION_JSON;
12
16
  }
13
17
  else {
14
- delete headers['Content-Type'];
18
+ if (isApp) {
19
+ headers['Content-Type'] = ContentType.MULTIPART_FORM_DATA;
20
+ // add the app files
21
+ for (const fileKey in appFiles) {
22
+ const currentFile = appFiles[fileKey];
23
+ if (Array.isArray(currentFile)) {
24
+ for (const innerFile of currentFile) {
25
+ body.append(fileKey, innerFile);
26
+ }
27
+ }
28
+ else {
29
+ body.append(fileKey, currentFile);
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ delete headers['Content-Type'];
35
+ }
15
36
  }
16
37
  try {
17
- const axios = axiosInstance({ baseURL, headers, timeout });
38
+ const axiosRequest = axiosInstance({ baseURL, headers, timeout });
18
39
  // send request
19
- const resp = await axios({
40
+ const resp = await axiosRequest({
20
41
  url: path,
21
42
  method,
22
43
  data: body,
@@ -44,6 +65,19 @@ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, he
44
65
  ...errorData,
45
66
  });
46
67
  }
68
+ }
69
+ function getAppFiles(body, fileSelectors = []) {
70
+ const files = {};
71
+ if (body) {
72
+ if (fileSelectors.length > 0) {
73
+ //
74
+ for (const fileKey of fileSelectors) {
75
+ files[fileKey] = body[fileKey];
76
+ delete body[fileKey];
77
+ }
78
+ }
79
+ }
80
+ return files;
47
81
  }
48
82
 
49
83
  export { makeRequest };
@@ -1 +1 @@
1
- {"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,6 +8,11 @@ export interface IMakeRequest {
8
8
  method?: HttpMethod;
9
9
  isFormData?: boolean;
10
10
  headers: RawAxiosRequestHeaders;
11
+ appFileConfig?: AppFileConfig;
12
+ }
13
+ export interface AppFileConfig {
14
+ fileSelectors?: string[];
15
+ isApp: boolean;
11
16
  }
12
17
  export interface AxiosInstanceOption {
13
18
  bearerToken?: string;
@@ -1,14 +1,19 @@
1
1
  import type { RawAxiosRequestHeaders } from 'axios';
2
- export interface BootstrapQueryRequest {
2
+ export interface BootstrapConfig {
3
3
  environments?: {
4
4
  appBaseUrl: string;
5
5
  appTimeout: number;
6
6
  };
7
- context?: 'app' | 'web' | 'electronjs';
7
+ context?: ContextType;
8
+ modelConfig?: BootstrapModelConfig;
8
9
  }
10
+ export interface BootstrapModelConfig {
11
+ idColumn: string;
12
+ }
13
+ export type ContextType = 'app' | 'web' | 'electronjs';
9
14
  export interface TanstackQueryConfig {
10
15
  headers: RawAxiosRequestHeaders;
11
- options?: BootstrapQueryRequest;
16
+ options?: BootstrapConfig;
12
17
  }
13
18
  export interface IUseQueryHeaders {
14
19
  getHeaders: () => TanstackQueryConfig['headers'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ventlio/tanstack-query",
3
- "version": "0.2.63",
3
+ "version": "0.2.65",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "contributors": [
@@ -51,6 +51,8 @@
51
51
  "@testing-library/react-hooks": "^8.0.1",
52
52
  "@types/axios": "^0.14.0",
53
53
  "@types/jest": "^29.5.1",
54
+ "@types/lodash.result": "^4.5.7",
55
+ "@types/lodash.set": "^4.3.7",
54
56
  "@types/node": "*",
55
57
  "@types/react": "^18.2.0",
56
58
  "@types/react-dom": "^18.2.0",
@@ -82,5 +84,9 @@
82
84
  "files": [
83
85
  "dist/**/*",
84
86
  "src"
85
- ]
87
+ ],
88
+ "dependencies": {
89
+ "lodash.result": "^4.5.2",
90
+ "lodash.set": "^4.3.2"
91
+ }
86
92
  }
@@ -1,14 +1,14 @@
1
1
  import type { QueryClient } from '@tanstack/react-query';
2
- import type { BootstrapQueryRequest, TanstackQueryConfig } from '../types';
2
+ import type { BootstrapConfig, TanstackQueryConfig } from '../types';
3
3
 
4
- export const bootstrapQueryRequest = (queryClient: QueryClient, options?: BootstrapQueryRequest): void => {
4
+ export const bootstrapQueryRequest = (queryClient: QueryClient, options?: BootstrapConfig): void => {
5
5
  // make query config doesn't expire
6
6
  queryClient.setQueryDefaults(['config'], {
7
7
  staleTime: Infinity,
8
8
  cacheTime: Infinity,
9
9
  });
10
10
 
11
- // set default query confg
11
+ // set default query config
12
12
  queryClient.setQueryData<TanstackQueryConfig>(['config'], {
13
13
  headers: {
14
14
  Authorization: ``,
@@ -3,8 +3,8 @@ import { useReactNativeEnv } from './useReactNativeEnv';
3
3
 
4
4
  export const useEnvironmentVariables = (): IConfig => {
5
5
  const { appTimeout, appUrl } = useReactNativeEnv();
6
- const url = process.env.REACT_APP_API_URL || process.env.NEXT_PUBLIC_API_URL || appUrl;
7
- const timeout = process.env.REACT_APP_API_TIMEOUT || process.env.NEXT_PUBLIC_API_TIMEOUT || appTimeout;
6
+ const url = process.env.REACT_APP_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? appUrl;
7
+ const timeout = process.env.REACT_APP_API_TIMEOUT ?? process.env.NEXT_PUBLIC_API_TIMEOUT ?? appTimeout;
8
8
 
9
9
  return {
10
10
  API_URL: url as string,
@@ -1,28 +1,13 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
- import { useEffect, useState } from 'react';
3
2
  import type { TanstackQueryConfig } from '../types';
4
3
 
5
4
  export const useReactNativeEnv = () => {
6
5
  const queryClient = useQueryClient();
6
+ const config = queryClient.getQueryData<TanstackQueryConfig>(['config']);
7
7
 
8
- const [appUrl, setAppUrl] = useState<string | undefined>(undefined);
9
- const [appTimeout, setAppTimeout] = useState<number | undefined>();
8
+ const appUrl: string | undefined = config?.options?.environments?.appBaseUrl;
9
+ const appTimeout: number | undefined = config?.options?.environments?.appTimeout;
10
+ const isApp = config?.options?.context === 'app';
10
11
 
11
- useEffect(() => {
12
- const config = queryClient.getQueryData<TanstackQueryConfig>(['config']);
13
-
14
- const loadReactNativeEnvIfNeeded = async () => {
15
- if (config?.options?.context === 'app') {
16
- const API_URL = config.options.environments?.appBaseUrl;
17
- const API_TIMEOUT = config.options.environments?.appTimeout;
18
-
19
- setAppUrl(API_URL);
20
- setAppTimeout(API_TIMEOUT);
21
- }
22
- };
23
-
24
- loadReactNativeEnvIfNeeded();
25
- }, [queryClient]);
26
-
27
- return { appUrl, appTimeout };
12
+ return { appUrl, appTimeout, isApp };
28
13
  };
@@ -1,3 +1,4 @@
1
+ export * from './model.interface';
1
2
  export * from './useKeyTrackerModel';
2
3
  export * from './useQueryModel';
3
4
  export * from './useRefetchQuery';
@@ -0,0 +1,10 @@
1
+ export interface QueryModelBuilder<T> {
2
+ add: (data: T, position?: QueryModelAddPosition, path?: string) => T | undefined;
3
+ findAll: (path?: string) => T[] | undefined;
4
+ findMany: (selector: (record: T) => boolean, path?: string) => T[];
5
+ find: (id: number | string, path?: string) => T | undefined;
6
+ update: (id: number | string, data: Partial<T>, path?: string) => T | undefined;
7
+ remove: (id: number, path?: string) => boolean;
8
+ }
9
+
10
+ export type QueryModelAddPosition = 'start' | 'end';
@@ -1,8 +1,128 @@
1
- import type { QueryFilters } from '@tanstack/react-query';
2
1
  import { useQueryClient } from '@tanstack/react-query';
2
+ import result from 'lodash.result';
3
+ import set from 'lodash.set';
4
+ import type { TanstackQueryConfig } from '../types';
5
+ import type { QueryModelAddPosition, QueryModelBuilder } from './model.interface';
6
+ import { useKeyTrackerModel } from './useKeyTrackerModel';
3
7
 
4
- export const useQueryModel = (queryKey: any[], filters?: QueryFilters | undefined) => {
8
+ export const useQueryModel = <T>(keyTracker: string, exact: boolean = true): QueryModelBuilder<T> => {
5
9
  const queryClient = useQueryClient();
10
+ const { getQueryKey } = useKeyTrackerModel(keyTracker);
11
+ const queryKey = getQueryKey() as any[];
6
12
 
7
- return queryClient.getQueryData(queryKey, filters);
13
+ const add = (data: T, position?: QueryModelAddPosition, path?: string): T | undefined => {
14
+ let records = findAll(path) ?? [];
15
+
16
+ if (!position || position === 'end') {
17
+ records = [...records, data];
18
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
19
+ } else if (position === 'start') {
20
+ records = [data, ...records];
21
+ }
22
+
23
+ if (!path) {
24
+ queryClient.setQueryData(queryKey, records);
25
+ } else {
26
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
27
+ queryClient.setQueryData(queryKey, set(queryData, path, records));
28
+ }
29
+
30
+ return data;
31
+ };
32
+
33
+ const findAll = (path?: string): T[] | undefined => {
34
+ const data = queryClient.getQueryData(queryKey, { exact });
35
+
36
+ if (!data) {
37
+ return [];
38
+ }
39
+
40
+ if (!path) {
41
+ return Array.isArray(data) ? data : [data];
42
+ }
43
+
44
+ return result<T[]>(data, path, []);
45
+ };
46
+
47
+ const findMany = (selector: (record: T) => boolean, path?: string): T[] => {
48
+ const data = findAll(path) ?? [];
49
+ return data.filter(selector);
50
+ };
51
+
52
+ const find = (id: string | number, path?: string): T | undefined => {
53
+ const modelConfig = getModelConfig();
54
+
55
+ if (!modelConfig?.idColumn) {
56
+ return undefined;
57
+ }
58
+ const data = findAll(path) ?? [];
59
+
60
+ return data.find((record) => (record as Record<string, any>)[modelConfig.idColumn] === id);
61
+ };
62
+
63
+ const getModelConfig = () => {
64
+ const { options } = queryClient.getQueryData<TanstackQueryConfig>(['config']) ?? {};
65
+ const { modelConfig } = options ?? {};
66
+
67
+ return modelConfig;
68
+ };
69
+
70
+ const update = (id: string | number, data: Partial<T>, path?: string): T | undefined => {
71
+ const oldData = findAll(path) ?? [];
72
+ const modelConfig = getModelConfig();
73
+
74
+ if (!modelConfig?.idColumn) {
75
+ return undefined;
76
+ }
77
+ const idColumn = modelConfig.idColumn;
78
+
79
+ let updatedRecord: T | undefined = undefined;
80
+ const newData = oldData.map((record) => {
81
+ let dataRecord = record as Record<string, any>;
82
+
83
+ if (dataRecord[idColumn] === id) {
84
+ dataRecord = { ...dataRecord, ...data };
85
+ updatedRecord = dataRecord as T;
86
+ }
87
+
88
+ return dataRecord;
89
+ });
90
+
91
+ if (!path) {
92
+ queryClient.setQueryData(queryKey, newData);
93
+ } else {
94
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
95
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
96
+ }
97
+ return updatedRecord;
98
+ };
99
+
100
+ const remove = (id: number | string, path?: string): boolean => {
101
+ const oldData = findAll(path) ?? [];
102
+ const modelConfig = getModelConfig();
103
+
104
+ if (!modelConfig?.idColumn) {
105
+ return false;
106
+ }
107
+ const idColumn = modelConfig.idColumn;
108
+ let updated = false;
109
+ const newData = oldData.filter((record) => {
110
+ const dataRecord = record as Record<string, any>;
111
+ if (dataRecord[idColumn] === id) {
112
+ updated = true;
113
+ return false;
114
+ }
115
+ return true;
116
+ });
117
+
118
+ if (!path) {
119
+ queryClient.setQueryData(queryKey, newData);
120
+ } else {
121
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
122
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
123
+ }
124
+ return updated;
125
+ };
126
+
127
+ return { find, findAll, findMany, remove, update, add };
8
128
  };
@@ -63,7 +63,7 @@ export const useDeleteRequest = <TResponse>(deleteOptions?: DefaultRequestOption
63
63
  internalDeleteOptions.enabled = true;
64
64
 
65
65
  await updatedPathAsync(link);
66
- await setOptionsAsync(deleteOptions);
66
+ await setOptionsAsync(internalDeleteOptions);
67
67
 
68
68
  return query.data;
69
69
  };
@@ -1,6 +1,6 @@
1
1
  import type { MutateOptions } from '@tanstack/react-query';
2
2
  import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { useEnvironmentVariables, useQueryHeaders } from '../config';
3
+ import { useEnvironmentVariables, useQueryHeaders, useReactNativeEnv } from '../config';
4
4
 
5
5
  import type { RawAxiosRequestHeaders } from 'axios';
6
6
  import { scrollToTop } from '../helpers';
@@ -14,14 +14,16 @@ export const usePostRequest = <TResponse>({
14
14
  isFormData = false,
15
15
  baseUrl,
16
16
  headers,
17
+ fileSelectors,
17
18
  }: {
18
19
  path: string;
19
20
  isFormData?: boolean;
21
+ fileSelectors?: string[];
20
22
  } & DefaultRequestOptions) => {
21
23
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
22
24
  const queryClient = useQueryClient();
23
25
  const { getHeaders } = useQueryHeaders();
24
-
26
+ const { isApp } = useReactNativeEnv();
25
27
  const sendRequest = async (res: (value: any) => void, rej: (reason?: any) => void, postData: any) => {
26
28
  // get request headers
27
29
  const globalHeaders: RawAxiosRequestHeaders = getHeaders();
@@ -35,6 +37,10 @@ export const usePostRequest = <TResponse>({
35
37
  headers: { ...globalHeaders, ...headers },
36
38
  baseURL: baseUrl ?? API_URL,
37
39
  timeout: TIMEOUT,
40
+ appFileConfig: {
41
+ isApp,
42
+ fileSelectors,
43
+ },
38
44
  });
39
45
 
40
46
  if (postResponse.status) {
@@ -1,6 +1,6 @@
1
+ import axios from 'axios';
1
2
  import { axiosInstance } from './axios-instance';
2
3
 
3
- import { buildFormData } from './buildFormData';
4
4
  import { ContentType, HttpMethod } from './request.enum';
5
5
  import type { IMakeRequest } from './request.interface';
6
6
  import { errorTransformer, successTransformer } from './transformer';
@@ -13,22 +13,43 @@ export async function makeRequest<TResponse>({
13
13
  headers = {},
14
14
  baseURL,
15
15
  timeout,
16
+ appFileConfig,
16
17
  }: IMakeRequest) {
18
+ // check if file is included in mobile app environment and extract all file input to avoid
19
+ // it being formatted to object using axios formData builder
20
+ const isApp = appFileConfig?.isApp;
21
+ const appFiles: Record<string, string> = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
22
+
17
23
  // configure body
18
- body = isFormData ? buildFormData(body as Record<string, any>) : body;
24
+ body = (isFormData ? axios.toFormData(body as FormData) : body) as FormData;
19
25
 
20
- // configure request header
26
+ // configure request header1
21
27
  if (!isFormData) {
22
28
  headers['Content-Type'] = ContentType.APPLICATION_JSON;
23
29
  } else {
24
- delete headers['Content-Type'];
30
+ if (isApp) {
31
+ headers['Content-Type'] = ContentType.MULTIPART_FORM_DATA;
32
+ // add the app files
33
+ for (const fileKey in appFiles) {
34
+ const currentFile = appFiles[fileKey];
35
+ if (Array.isArray(currentFile)) {
36
+ for (const innerFile of currentFile) {
37
+ body.append(fileKey, innerFile);
38
+ }
39
+ } else {
40
+ body.append(fileKey, currentFile);
41
+ }
42
+ }
43
+ } else {
44
+ delete headers['Content-Type'];
45
+ }
25
46
  }
26
47
 
27
48
  try {
28
- const axios = axiosInstance({ baseURL, headers, timeout });
49
+ const axiosRequest = axiosInstance({ baseURL, headers, timeout });
29
50
 
30
51
  // send request
31
- const resp = await axios({
52
+ const resp = await axiosRequest({
32
53
  url: path,
33
54
  method,
34
55
  data: body,
@@ -60,3 +81,18 @@ export async function makeRequest<TResponse>({
60
81
  });
61
82
  }
62
83
  }
84
+ function getAppFiles(body: any, fileSelectors: string[] = []) {
85
+ const files: Record<string, string> = {};
86
+
87
+ if (body) {
88
+ if (fileSelectors.length > 0) {
89
+ //
90
+ for (const fileKey of fileSelectors) {
91
+ files[fileKey] = body[fileKey];
92
+ delete body[fileKey];
93
+ }
94
+ }
95
+ }
96
+
97
+ return files;
98
+ }
@@ -9,8 +9,13 @@ export interface IMakeRequest {
9
9
  method?: HttpMethod;
10
10
  isFormData?: boolean;
11
11
  headers: RawAxiosRequestHeaders;
12
+ appFileConfig?: AppFileConfig;
12
13
  }
13
14
 
15
+ export interface AppFileConfig {
16
+ fileSelectors?: string[];
17
+ isApp: boolean;
18
+ }
14
19
  export interface AxiosInstanceOption {
15
20
  bearerToken?: string;
16
21
  contentType?: string;
@@ -1,16 +1,22 @@
1
1
  import type { RawAxiosRequestHeaders } from 'axios';
2
2
 
3
- export interface BootstrapQueryRequest {
3
+ export interface BootstrapConfig {
4
4
  environments?: {
5
5
  appBaseUrl: string;
6
6
  appTimeout: number;
7
7
  };
8
- context?: 'app' | 'web' | 'electronjs';
8
+ context?: ContextType;
9
+ modelConfig?: BootstrapModelConfig;
9
10
  }
10
11
 
12
+ export interface BootstrapModelConfig {
13
+ idColumn: string;
14
+ }
15
+
16
+ export type ContextType = 'app' | 'web' | 'electronjs';
11
17
  export interface TanstackQueryConfig {
12
18
  headers: RawAxiosRequestHeaders;
13
- options?: BootstrapQueryRequest;
19
+ options?: BootstrapConfig;
14
20
  }
15
21
 
16
22
  export interface IUseQueryHeaders {