@ventlio/tanstack-query 0.5.11 → 0.5.13
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/README.md +178 -150
- package/dist/config/bootstrapQueryRequest.d.ts +7 -1
- package/dist/config/useEnvironmentVariables.d.ts +4 -0
- package/dist/index.mjs +307 -90
- package/dist/index.mjs.map +1 -1
- package/dist/node_modules/@tanstack/react-store/dist/esm/index.js +1 -1
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/cjs/use-sync-external-store-shim/with-selector.development.js +1 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +1 -0
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/cjs/use-sync-external-store-shim/with-selector.production.js +1 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.js.map +1 -0
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/cjs/use-sync-external-store-shim.development.js +1 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +1 -0
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/cjs/use-sync-external-store-shim.production.js +1 -1
- package/dist/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js.map +1 -0
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/shim/index.js +1 -1
- package/dist/node_modules/use-sync-external-store/shim/index.js.map +1 -0
- package/dist/node_modules/{@tanstack/react-store/node_modules/use-sync-external-store → use-sync-external-store}/shim/with-selector.js +1 -1
- package/dist/node_modules/use-sync-external-store/shim/with-selector.js.map +1 -0
- package/dist/queries/useGetRequest.d.ts +12 -2
- package/dist/request/make-request.d.ts +12 -1
- package/dist/src/config/bootstrapQueryRequest.js +42 -2
- package/dist/src/config/bootstrapQueryRequest.js.map +1 -1
- package/dist/src/config/useEnvironmentVariables.js +34 -3
- package/dist/src/config/useEnvironmentVariables.js.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/queries/useDeleteRequest.js +19 -14
- package/dist/src/queries/useDeleteRequest.js.map +1 -1
- package/dist/src/queries/useGetInfiniteRequest.js +17 -12
- package/dist/src/queries/useGetInfiniteRequest.js.map +1 -1
- package/dist/src/queries/useGetRequest.js +86 -28
- package/dist/src/queries/useGetRequest.js.map +1 -1
- package/dist/src/queries/usePatchRequest.js +19 -14
- package/dist/src/queries/usePatchRequest.js.map +1 -1
- package/dist/src/queries/usePostRequest.js +18 -13
- package/dist/src/queries/usePostRequest.js.map +1 -1
- package/dist/src/request/make-request.js +75 -6
- package/dist/src/request/make-request.js.map +1 -1
- package/dist/types/index.d.ts +24 -5
- package/package.json +2 -2
- package/src/config/bootstrapQueryRequest.ts +48 -3
- package/src/config/useEnvironmentVariables.ts +41 -3
- package/src/queries/useDeleteRequest.ts +18 -20
- package/src/queries/useGetInfiniteRequest.ts +17 -17
- package/src/queries/useGetRequest.ts +109 -33
- package/src/queries/usePatchRequest.ts +19 -16
- package/src/queries/usePostRequest.ts +18 -15
- package/src/queries/usePutRequest.ts +16 -15
- package/src/request/make-request.ts +112 -15
- package/src/types/index.ts +38 -4
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +0 -1
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.js.map +0 -1
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +0 -1
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js.map +0 -1
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/shim/index.js.map +0 -1
- package/dist/node_modules/@tanstack/react-store/node_modules/use-sync-external-store/shim/with-selector.js.map +0 -1
|
@@ -16,7 +16,7 @@ const useGetInfiniteRequest = ({ path, load = false, queryOptions, keyTracker, b
|
|
|
16
16
|
const globalHeaders = useHeaderStore((state) => state.headers);
|
|
17
17
|
const [requestPath, setRequestPath] = useState(path);
|
|
18
18
|
const [options, setOptions] = useState(queryOptions);
|
|
19
|
-
|
|
19
|
+
useStore(bootStore);
|
|
20
20
|
const [requestPayload, setRequestPayload] = useState();
|
|
21
21
|
const isFutureQueriesPaused = usePauseFutureRequests((state) => state.isFutureQueriesPaused);
|
|
22
22
|
let queryClient = useQueryClient();
|
|
@@ -31,17 +31,22 @@ const useGetInfiniteRequest = ({ path, load = false, queryOptions, keyTracker, b
|
|
|
31
31
|
baseURL: baseUrl ?? API_URL,
|
|
32
32
|
timeout: TIMEOUT,
|
|
33
33
|
};
|
|
34
|
-
let getResponse
|
|
35
|
-
if (middleware) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
// let getResponse: IRequestError | IRequestSuccess<TResponse>;
|
|
35
|
+
// if (middleware) {
|
|
36
|
+
// // perform global middleware
|
|
37
|
+
// getResponse = await middleware(
|
|
38
|
+
// async (middlewareOptions) =>
|
|
39
|
+
// await makeRequest<TResponse>(
|
|
40
|
+
// middlewareOptions ? { ...requestOptions, ...middlewareOptions } : requestOptions
|
|
41
|
+
// ),
|
|
42
|
+
// {
|
|
43
|
+
// path,
|
|
44
|
+
// baseUrl: baseUrl ?? API_URL,
|
|
45
|
+
// }
|
|
46
|
+
// );
|
|
47
|
+
// } else {
|
|
48
|
+
const getResponse = await makeRequest(requestOptions);
|
|
49
|
+
// }
|
|
45
50
|
if (getResponse.status) {
|
|
46
51
|
res(getResponse);
|
|
47
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGetInfiniteRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGetInfiniteRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -8,21 +8,29 @@ import { useHeaderStore } from '../stores/useHeaderStore.js';
|
|
|
8
8
|
import { usePauseFutureRequests } from '../stores/usePauseFutureRequests.js';
|
|
9
9
|
import { useStore } from '../../node_modules/@tanstack/react-store/dist/esm/index.js';
|
|
10
10
|
import 'axios';
|
|
11
|
-
import { makeRequest } from '../request/make-request.js';
|
|
11
|
+
import { executeMiddlewareChain, makeRequest } from '../request/make-request.js';
|
|
12
12
|
import '../request/request.enum.js';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Hook for making GET requests with pagination support
|
|
16
|
+
*/
|
|
17
|
+
const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl, headers, paginationConfig, }) => {
|
|
15
18
|
const [requestPath, setRequestPath] = useState(path);
|
|
16
19
|
const [options, setOptions] = useState(queryOptions);
|
|
17
20
|
const [page, setPage] = useState(1);
|
|
18
21
|
const { API_URL, TIMEOUT } = useEnvironmentVariables();
|
|
19
|
-
const { middleware } = useStore(bootStore);
|
|
22
|
+
const { middleware, pagination: globalPaginationConfig } = useStore(bootStore);
|
|
20
23
|
const globalHeaders = useHeaderStore((state) => state.headers);
|
|
21
24
|
const [requestPayload, setRequestPayload] = useState();
|
|
22
25
|
const isFutureQueriesPaused = usePauseFutureRequests((state) => state.isFutureQueriesPaused);
|
|
23
26
|
let queryClient = useQueryClient();
|
|
24
27
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
28
|
queryClient = useMemo(() => queryClient, []);
|
|
29
|
+
// Merge global and local pagination config
|
|
30
|
+
const pagination = useMemo(() => ({
|
|
31
|
+
...globalPaginationConfig,
|
|
32
|
+
...paginationConfig,
|
|
33
|
+
}), [globalPaginationConfig, paginationConfig]);
|
|
26
34
|
const sendRequest = async (res, rej, queryKey) => {
|
|
27
35
|
const [url] = queryKey;
|
|
28
36
|
const requestUrl = (url ?? requestPath);
|
|
@@ -32,15 +40,23 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
|
|
|
32
40
|
baseURL: baseUrl ?? API_URL,
|
|
33
41
|
timeout: TIMEOUT,
|
|
34
42
|
};
|
|
43
|
+
// Create the final handler that makes the actual request
|
|
44
|
+
const finalHandler = async (options) => {
|
|
45
|
+
const finalOptions = options ? { ...requestOptions, ...options } : requestOptions;
|
|
46
|
+
return await makeRequest(finalOptions);
|
|
47
|
+
};
|
|
35
48
|
let getResponse;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
path,
|
|
49
|
+
// If middleware is available, execute the middleware chain
|
|
50
|
+
if (middleware && Array.isArray(middleware) && middleware.length > 0) {
|
|
51
|
+
const context = {
|
|
40
52
|
baseUrl: baseUrl ?? API_URL,
|
|
41
|
-
|
|
53
|
+
path: requestUrl,
|
|
54
|
+
options: requestOptions,
|
|
55
|
+
};
|
|
56
|
+
getResponse = await executeMiddlewareChain(middleware, context, finalHandler);
|
|
42
57
|
}
|
|
43
58
|
else {
|
|
59
|
+
// Otherwise, just make the request directly
|
|
44
60
|
getResponse = await makeRequest(requestOptions);
|
|
45
61
|
}
|
|
46
62
|
if (getResponse.status) {
|
|
@@ -70,34 +86,72 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
|
|
|
70
86
|
queryClient.setQueryData([keyTracker], [requestPath, {}]);
|
|
71
87
|
}
|
|
72
88
|
}, [keyTracker, requestPath, queryClient, queryOptions?.staleTime]);
|
|
89
|
+
/**
|
|
90
|
+
* Extract pagination data from response using configured extractor
|
|
91
|
+
*/
|
|
92
|
+
const getPaginationData = (response) => {
|
|
93
|
+
// Use the configured pagination extractor or fall back to default
|
|
94
|
+
const extractPagination = pagination.extractPagination ||
|
|
95
|
+
((res) => {
|
|
96
|
+
if ('pagination' in res.data) {
|
|
97
|
+
return res.data.pagination;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
});
|
|
101
|
+
return extractPagination(response);
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Navigate to the next page if available
|
|
105
|
+
*/
|
|
73
106
|
const nextPage = () => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
107
|
+
// The linter thinks query.data is always falsy, but we know it can be defined after a successful query
|
|
108
|
+
// Let's restructure to avoid the conditional
|
|
109
|
+
const paginationData = query.data && getPaginationData(query.data);
|
|
110
|
+
if (!paginationData)
|
|
111
|
+
return;
|
|
112
|
+
if (paginationData.next_page !== paginationData.current_page &&
|
|
113
|
+
paginationData.next_page > paginationData.current_page) {
|
|
114
|
+
setRequestPath(constructPaginationLink(requestPath, paginationData.next_page));
|
|
79
115
|
}
|
|
80
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Navigate to the previous page if available
|
|
119
|
+
*/
|
|
81
120
|
const prevPage = () => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
// The linter thinks query.data is always falsy, but we know it can be defined after a successful query
|
|
122
|
+
// Let's restructure to avoid the conditional
|
|
123
|
+
const paginationData = query.data && getPaginationData(query.data);
|
|
124
|
+
if (!paginationData)
|
|
125
|
+
return;
|
|
126
|
+
if (paginationData.previous_page !== paginationData.current_page &&
|
|
127
|
+
paginationData.previous_page < paginationData.current_page) {
|
|
128
|
+
setRequestPath(constructPaginationLink(requestPath, paginationData.previous_page));
|
|
87
129
|
}
|
|
88
130
|
};
|
|
131
|
+
/**
|
|
132
|
+
* Construct a pagination URL using the configured builder
|
|
133
|
+
*/
|
|
89
134
|
const constructPaginationLink = (link, pageNumber) => {
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
135
|
+
// Use the configured pagination URL builder or fall back to default
|
|
136
|
+
const buildPaginationUrl = pagination.buildPaginationUrl ||
|
|
137
|
+
((url, page) => {
|
|
138
|
+
const [pathname, queryString] = url.split('?');
|
|
139
|
+
const queryParams = new URLSearchParams(queryString || '');
|
|
140
|
+
const pageParamName = pagination.pageParamName || 'page';
|
|
141
|
+
const oldPage = Number(queryParams.get(pageParamName));
|
|
142
|
+
queryParams.set(pageParamName, String(page));
|
|
143
|
+
const newUrl = pathname + '?' + queryParams.toString();
|
|
144
|
+
// only update page when pagination number changed
|
|
145
|
+
if (oldPage !== pageNumber) {
|
|
146
|
+
setPage(pageNumber);
|
|
147
|
+
}
|
|
148
|
+
return newUrl;
|
|
149
|
+
});
|
|
150
|
+
return buildPaginationUrl(link, pageNumber);
|
|
100
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* Navigate to a specific page
|
|
154
|
+
*/
|
|
101
155
|
const gotoPage = (pageNumber) => {
|
|
102
156
|
setRequestPath(constructPaginationLink(requestPath, pageNumber));
|
|
103
157
|
};
|
|
@@ -139,6 +193,10 @@ const useGetRequest = ({ path, load = false, queryOptions, keyTracker, baseUrl,
|
|
|
139
193
|
gotoPage,
|
|
140
194
|
page,
|
|
141
195
|
queryKey: [requestPath, {}],
|
|
196
|
+
// Add pagination data accessor - restructured to avoid linter error
|
|
197
|
+
getPaginationData: function () {
|
|
198
|
+
return query.data ? getPaginationData(query.data) : undefined;
|
|
199
|
+
},
|
|
142
200
|
};
|
|
143
201
|
};
|
|
144
202
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGetRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGetRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -19,7 +19,7 @@ const usePatchRequest = ({ path, baseUrl, headers }) => {
|
|
|
19
19
|
const globalHeaders = useHeaderStore((state) => state.headers);
|
|
20
20
|
const [requestPayload, setRequestPayload] = useState();
|
|
21
21
|
const isFutureMutationsPaused = usePauseFutureRequests((state) => state.isFutureMutationsPaused);
|
|
22
|
-
const {
|
|
22
|
+
const { context } = useStore(bootStore);
|
|
23
23
|
const sendRequest = async (res, rej, data) => {
|
|
24
24
|
// get request headers
|
|
25
25
|
const requestOptions = {
|
|
@@ -31,19 +31,24 @@ const usePatchRequest = ({ path, baseUrl, headers }) => {
|
|
|
31
31
|
timeout: TIMEOUT,
|
|
32
32
|
onUploadProgress,
|
|
33
33
|
};
|
|
34
|
-
let patchResponse
|
|
35
|
-
if (middleware) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
34
|
+
// let patchResponse: IRequestError | IRequestSuccess<TResponse>;
|
|
35
|
+
// if (middleware) {
|
|
36
|
+
// // perform global middleware
|
|
37
|
+
// const middlewareResponse = await middleware(
|
|
38
|
+
// async (options) =>
|
|
39
|
+
// await makeRequest<TResponse>(
|
|
40
|
+
// options ? { ...requestOptions, ...options } : requestOptions
|
|
41
|
+
// ),
|
|
42
|
+
// {
|
|
43
|
+
// path,
|
|
44
|
+
// baseUrl: baseUrl ?? API_URL,
|
|
45
|
+
// body: data,
|
|
46
|
+
// }
|
|
47
|
+
// );
|
|
48
|
+
// patchResponse = middlewareResponse;
|
|
49
|
+
// } else {
|
|
50
|
+
const patchResponse = await makeRequest(requestOptions);
|
|
51
|
+
// }
|
|
47
52
|
if (patchResponse.status) {
|
|
48
53
|
// scroll to top after success
|
|
49
54
|
if (context !== 'app') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -16,7 +16,7 @@ import { HttpMethod } from '../request/request.enum.js';
|
|
|
16
16
|
|
|
17
17
|
const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelectors, }) => {
|
|
18
18
|
const { API_URL, TIMEOUT } = useEnvironmentVariables();
|
|
19
|
-
const {
|
|
19
|
+
const { context } = useStore(bootStore);
|
|
20
20
|
const globalHeaders = useHeaderStore((state) => state.headers);
|
|
21
21
|
const { isApp } = useReactNativeEnv();
|
|
22
22
|
const { uploadProgressPercent, onUploadProgress } = useUploadProgress();
|
|
@@ -41,18 +41,23 @@ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelect
|
|
|
41
41
|
onUploadProgress,
|
|
42
42
|
...requestConfig,
|
|
43
43
|
};
|
|
44
|
-
let postResponse
|
|
45
|
-
if (middleware) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
// let postResponse: IRequestError | IRequestSuccess<TResponse>;
|
|
45
|
+
// if (middleware) {
|
|
46
|
+
// // perform global middleware
|
|
47
|
+
// postResponse = await middleware(
|
|
48
|
+
// async (options) =>
|
|
49
|
+
// await makeRequest<TResponse>(
|
|
50
|
+
// options ? { ...requestOptions, ...options } : requestOptions
|
|
51
|
+
// ),
|
|
52
|
+
// {
|
|
53
|
+
// path,
|
|
54
|
+
// baseUrl: baseUrl ?? API_URL,
|
|
55
|
+
// body: data,
|
|
56
|
+
// }
|
|
57
|
+
// );
|
|
58
|
+
// } else {
|
|
59
|
+
const postResponse = await makeRequest(requestOptions);
|
|
60
|
+
// }
|
|
56
61
|
if (postResponse.status) {
|
|
57
62
|
// scroll to top after success
|
|
58
63
|
if (context !== 'app') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -3,23 +3,92 @@ import { axiosInstance } from './axios-instance.js';
|
|
|
3
3
|
import { HttpMethod, ContentType } from './request.enum.js';
|
|
4
4
|
import { errorTransformer, successTransformer } from './transformer.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Execute a chain of middleware functions
|
|
8
|
+
*/
|
|
9
|
+
async function executeMiddlewareChain(middlewares, context, finalHandler) {
|
|
10
|
+
// Create a chain of middleware functions
|
|
11
|
+
const chain = middlewares.reduceRight((next, middleware) => {
|
|
12
|
+
return (options) => {
|
|
13
|
+
// Update context with new options if provided
|
|
14
|
+
const updatedContext = options ? { ...context, options: { ...context.options, ...options } } : context;
|
|
15
|
+
return middleware(updatedContext, next);
|
|
16
|
+
};
|
|
17
|
+
}, finalHandler);
|
|
18
|
+
// Execute the middleware chain
|
|
19
|
+
return await chain(undefined);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Make an HTTP request with middleware support
|
|
23
|
+
*
|
|
24
|
+
* @param requestOptions - Request options
|
|
25
|
+
* @param middlewares - Optional array of middleware functions
|
|
26
|
+
*/
|
|
27
|
+
async function makeRequest(requestOptions, middlewares) {
|
|
28
|
+
const { body = {}, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, appFileConfig, onUploadProgress, } = requestOptions;
|
|
7
29
|
// check if file is included in mobile app environment and extract all file input to avoid
|
|
8
30
|
// it being formatted to object using axios formData builder
|
|
9
31
|
const isApp = appFileConfig?.isApp;
|
|
10
32
|
const appFiles = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
|
|
11
33
|
// configure body
|
|
12
|
-
|
|
13
|
-
// configure request
|
|
14
|
-
configureRequestHeader(isFormData, headers, isApp, appFiles,
|
|
34
|
+
const processedBody = (isFormData ? axios.toFormData(body) : body);
|
|
35
|
+
// configure request header
|
|
36
|
+
configureRequestHeader(isFormData, headers, isApp, appFiles, processedBody);
|
|
37
|
+
// Create the final handler that makes the actual request
|
|
38
|
+
const finalHandler = async (options) => {
|
|
39
|
+
const finalRequestOptions = options
|
|
40
|
+
? {
|
|
41
|
+
...requestOptions,
|
|
42
|
+
body: processedBody,
|
|
43
|
+
...options,
|
|
44
|
+
}
|
|
45
|
+
: {
|
|
46
|
+
...requestOptions,
|
|
47
|
+
body: processedBody,
|
|
48
|
+
};
|
|
49
|
+
return await performRequest(finalRequestOptions);
|
|
50
|
+
};
|
|
51
|
+
// If middleware is available, execute the middleware chain
|
|
52
|
+
if (middlewares && middlewares.length > 0) {
|
|
53
|
+
const context = {
|
|
54
|
+
baseUrl: baseURL,
|
|
55
|
+
path,
|
|
56
|
+
body: body,
|
|
57
|
+
method,
|
|
58
|
+
headers,
|
|
59
|
+
options: {
|
|
60
|
+
baseURL,
|
|
61
|
+
timeout,
|
|
62
|
+
path,
|
|
63
|
+
body: processedBody,
|
|
64
|
+
method,
|
|
65
|
+
isFormData,
|
|
66
|
+
headers,
|
|
67
|
+
appFileConfig,
|
|
68
|
+
onUploadProgress,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return await executeMiddlewareChain(middlewares, context, finalHandler);
|
|
72
|
+
}
|
|
73
|
+
// Otherwise, just make the request directly
|
|
74
|
+
return await finalHandler(undefined);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Perform the actual HTTP request
|
|
78
|
+
*/
|
|
79
|
+
async function performRequest({ body, method, path, isFormData, headers, baseURL, timeout, appFileConfig, onUploadProgress, }) {
|
|
15
80
|
try {
|
|
16
81
|
const axiosRequest = axiosInstance({ baseURL, headers, timeout });
|
|
82
|
+
const isApp = appFileConfig?.isApp;
|
|
17
83
|
const axiosRequestConfig = {
|
|
18
84
|
url: path,
|
|
19
85
|
method,
|
|
20
86
|
onUploadProgress,
|
|
21
87
|
};
|
|
22
|
-
if
|
|
88
|
+
// Check if body exists and is not null
|
|
89
|
+
if (body &&
|
|
90
|
+
((typeof body === 'object' && Object.keys(body).length > 0) ||
|
|
91
|
+
(isFormData && !isApp && body instanceof FormData && Array.from(body.keys()).length > 0))) {
|
|
23
92
|
axiosRequestConfig.data = body;
|
|
24
93
|
}
|
|
25
94
|
// send request
|
|
@@ -85,5 +154,5 @@ function getAppFiles(body, fileSelectors = []) {
|
|
|
85
154
|
return files;
|
|
86
155
|
}
|
|
87
156
|
|
|
88
|
-
export { makeRequest };
|
|
157
|
+
export { executeMiddlewareChain, makeRequest };
|
|
89
158
|
//# sourceMappingURL=make-request.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { AxiosProgressEvent, RawAxiosRequestHeaders } from 'axios';
|
|
2
2
|
import type { AppFileConfig, HttpMethod, IMakeRequest, IRequestError, IRequestSuccess } from '../request';
|
|
3
|
+
import type { IPagination } from '../queries';
|
|
4
|
+
export type MiddlewareFunction<T = any> = (context: MiddlewareContext<T>, next: MiddlewareNext<T>) => Promise<IRequestError | IRequestSuccess<T>>;
|
|
5
|
+
export interface MiddlewareContext<T = any> {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
path: string;
|
|
8
|
+
body?: Record<string, any>;
|
|
9
|
+
method?: HttpMethod;
|
|
10
|
+
headers?: RawAxiosRequestHeaders;
|
|
11
|
+
options?: Partial<NextOptions>;
|
|
12
|
+
response?: IRequestError | IRequestSuccess<T>;
|
|
13
|
+
}
|
|
14
|
+
export type MiddlewareNext<T = any> = (options?: Partial<NextOptions>) => Promise<IRequestError | IRequestSuccess<T>>;
|
|
15
|
+
export type LegacyMiddlewareFunction<T = any> = (next: (options?: Partial<NextOptions>) => Promise<IRequestSuccess<T> | IRequestError>, configs?: {
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
path: string;
|
|
18
|
+
body?: Record<string, any>;
|
|
19
|
+
}) => Promise<IRequestError | IRequestSuccess<T>>;
|
|
3
20
|
export interface BootstrapConfig {
|
|
4
21
|
environments?: {
|
|
5
22
|
appBaseUrl: string;
|
|
@@ -7,11 +24,13 @@ export interface BootstrapConfig {
|
|
|
7
24
|
};
|
|
8
25
|
context?: ContextType;
|
|
9
26
|
modelConfig?: BootstrapModelConfig;
|
|
10
|
-
middleware?:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
middleware?: MiddlewareFunction[] | LegacyMiddlewareFunction;
|
|
28
|
+
pagination?: PaginationConfig;
|
|
29
|
+
}
|
|
30
|
+
export interface PaginationConfig {
|
|
31
|
+
extractPagination?: <T>(response: IRequestSuccess<T>) => IPagination | undefined;
|
|
32
|
+
buildPaginationUrl?: (url: string, page: number) => string;
|
|
33
|
+
pageParamName?: string;
|
|
15
34
|
}
|
|
16
35
|
export interface NextOptions extends Partial<IMakeRequest> {
|
|
17
36
|
baseURL: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ventlio/tanstack-query",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"contributors": [
|
|
@@ -91,6 +91,6 @@
|
|
|
91
91
|
"lodash.result": "^4.5.2",
|
|
92
92
|
"lodash.set": "^4.3.2",
|
|
93
93
|
"url-search-params-polyfill": "^8.2.5",
|
|
94
|
-
"zustand": "^
|
|
94
|
+
"zustand": "^5.0.4"
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
import type { QueryClient } from '@tanstack/react-query';
|
|
2
2
|
import 'url-search-params-polyfill';
|
|
3
|
-
import type { BootstrapConfig } from '../types';
|
|
3
|
+
import type { BootstrapConfig, LegacyMiddlewareFunction, MiddlewareFunction } from '../types';
|
|
4
4
|
import { bootStore } from './bootStore';
|
|
5
|
+
import type { IPagination } from '../queries';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Bootstrap the query request system with configuration options
|
|
9
|
+
*
|
|
10
|
+
* @param queryClient - TanStack Query client instance
|
|
11
|
+
* @param options - Configuration options
|
|
12
|
+
*/
|
|
13
|
+
export const bootstrapQueryRequest = async (queryClient: QueryClient, options: BootstrapConfig = {}): Promise<void> => {
|
|
14
|
+
// Resume any paused mutations
|
|
8
15
|
await queryClient.resumePausedMutations();
|
|
9
16
|
|
|
17
|
+
// Set default pagination configuration if not provided
|
|
18
|
+
if (!options.pagination) {
|
|
19
|
+
options.pagination = {
|
|
20
|
+
pageParamName: 'page',
|
|
21
|
+
buildPaginationUrl: (url: string, page: number) => {
|
|
22
|
+
const [pathname, queryString] = url.split('?');
|
|
23
|
+
const queryParams = new URLSearchParams(queryString);
|
|
24
|
+
queryParams.set('page', String(page));
|
|
25
|
+
return pathname + '?' + queryParams.toString();
|
|
26
|
+
},
|
|
27
|
+
extractPagination: (response: any) => {
|
|
28
|
+
// Default pagination extraction from response
|
|
29
|
+
if (response.data && 'pagination' in response.data) {
|
|
30
|
+
return response.data.pagination as IPagination;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Convert legacy middleware to new format if needed
|
|
38
|
+
if (options.middleware && !Array.isArray(options.middleware)) {
|
|
39
|
+
const legacyMiddleware = options.middleware as LegacyMiddlewareFunction;
|
|
40
|
+
|
|
41
|
+
// Create a new middleware function that adapts the legacy format
|
|
42
|
+
const adaptedMiddleware: MiddlewareFunction = async (context, next) => {
|
|
43
|
+
return await legacyMiddleware((opts) => next(opts), {
|
|
44
|
+
baseUrl: context.baseUrl,
|
|
45
|
+
path: context.path,
|
|
46
|
+
body: context.body,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Replace with array containing the adapted middleware
|
|
51
|
+
options.middleware = [adaptedMiddleware];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Store the configuration
|
|
10
55
|
bootStore.setState(() => options);
|
|
11
56
|
};
|
|
@@ -1,15 +1,53 @@
|
|
|
1
|
+
import { useStore } from '@tanstack/react-store';
|
|
1
2
|
import { useBaseUrlStore } from '../stores/useBaseUrlStore';
|
|
3
|
+
import { bootStore } from './bootStore';
|
|
2
4
|
import type { IConfig } from './config.interface';
|
|
3
5
|
import { useReactNativeEnv } from './useReactNativeEnv';
|
|
4
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Hook to access environment variables across different frameworks
|
|
9
|
+
* Supports React (CRA), Next.js, Vite, and React Native
|
|
10
|
+
*/
|
|
5
11
|
export const useEnvironmentVariables = (): IConfig => {
|
|
6
12
|
const { appTimeout, appUrl } = useReactNativeEnv();
|
|
7
13
|
const { baseUrl } = useBaseUrlStore();
|
|
8
|
-
const
|
|
9
|
-
|
|
14
|
+
const { environments } = useStore(bootStore);
|
|
15
|
+
|
|
16
|
+
// Framework environment variables detection
|
|
17
|
+
// Order of precedence:
|
|
18
|
+
// 1. Runtime baseUrl (set via useBaseUrlStore)
|
|
19
|
+
// 2. Bootstrap config environments
|
|
20
|
+
// 3. Framework-specific environment variables
|
|
21
|
+
// 4. React Native app URL
|
|
22
|
+
|
|
23
|
+
// Get global object to check for various environment variables
|
|
24
|
+
const globalObj = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : {};
|
|
25
|
+
|
|
26
|
+
// Check for Vite environment variables (without using import.meta directly)
|
|
27
|
+
// @ts-ignore - Access potential Vite environment variables
|
|
28
|
+
const viteEnv = globalObj.__VITE_ENV__ || {};
|
|
29
|
+
const viteApiUrl = viteEnv.VITE_API_URL;
|
|
30
|
+
const viteApiTimeout = viteEnv.VITE_API_TIMEOUT;
|
|
31
|
+
|
|
32
|
+
// Get URL with fallbacks
|
|
33
|
+
const url =
|
|
34
|
+
baseUrl ??
|
|
35
|
+
environments?.appBaseUrl ??
|
|
36
|
+
process.env.REACT_APP_API_URL ??
|
|
37
|
+
process.env.NEXT_PUBLIC_API_URL ??
|
|
38
|
+
viteApiUrl ??
|
|
39
|
+
appUrl;
|
|
40
|
+
|
|
41
|
+
// Get timeout with fallbacks
|
|
42
|
+
const timeout =
|
|
43
|
+
environments?.appTimeout ??
|
|
44
|
+
process.env.REACT_APP_API_TIMEOUT ??
|
|
45
|
+
process.env.NEXT_PUBLIC_API_TIMEOUT ??
|
|
46
|
+
viteApiTimeout ??
|
|
47
|
+
appTimeout;
|
|
10
48
|
|
|
11
49
|
return {
|
|
12
50
|
API_URL: url as string,
|
|
13
|
-
TIMEOUT: Number(timeout),
|
|
51
|
+
TIMEOUT: Number(timeout) || 30000, // Default timeout of 30 seconds
|
|
14
52
|
};
|
|
15
53
|
};
|