@openapi-qraft/react 1.1.5 → 1.3.2

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 (193) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +142 -46
  3. package/dist/QraftContext.d.ts +14 -5
  4. package/dist/QraftContext.d.ts.map +1 -1
  5. package/dist/QraftContext.js.map +1 -1
  6. package/dist/ServiceOperation.d.ts +112 -3
  7. package/dist/ServiceOperation.d.ts.map +1 -1
  8. package/dist/callbacks/cancelQueries.d.ts +2 -2
  9. package/dist/callbacks/cancelQueries.d.ts.map +1 -1
  10. package/dist/callbacks/cancelQueries.js.map +1 -1
  11. package/dist/callbacks/fetchInfiniteQuery.d.ts +7 -0
  12. package/dist/callbacks/fetchInfiniteQuery.d.ts.map +1 -0
  13. package/dist/callbacks/fetchInfiniteQuery.js +5 -0
  14. package/dist/callbacks/fetchInfiniteQuery.js.map +1 -0
  15. package/dist/callbacks/fetchQuery.d.ts +7 -0
  16. package/dist/callbacks/fetchQuery.d.ts.map +1 -0
  17. package/dist/callbacks/fetchQuery.js +5 -0
  18. package/dist/callbacks/fetchQuery.js.map +1 -0
  19. package/dist/callbacks/getInfiniteQueryData.d.ts +4 -4
  20. package/dist/callbacks/getInfiniteQueryData.d.ts.map +1 -1
  21. package/dist/callbacks/getInfiniteQueryData.js +3 -6
  22. package/dist/callbacks/getInfiniteQueryData.js.map +1 -1
  23. package/dist/callbacks/getInfiniteQueryKey.d.ts +2 -2
  24. package/dist/callbacks/getInfiniteQueryKey.d.ts.map +1 -1
  25. package/dist/callbacks/getInfiniteQueryKey.js.map +1 -1
  26. package/dist/callbacks/getMutationKey.d.ts +2 -2
  27. package/dist/callbacks/getMutationKey.d.ts.map +1 -1
  28. package/dist/callbacks/getMutationKey.js.map +1 -1
  29. package/dist/callbacks/getQueryData.d.ts +3 -3
  30. package/dist/callbacks/getQueryData.d.ts.map +1 -1
  31. package/dist/callbacks/getQueryData.js +3 -4
  32. package/dist/callbacks/getQueryData.js.map +1 -1
  33. package/dist/callbacks/getQueryKey.d.ts +2 -2
  34. package/dist/callbacks/getQueryKey.d.ts.map +1 -1
  35. package/dist/callbacks/getQueryKey.js.map +1 -1
  36. package/dist/callbacks/invalidateQueries.d.ts +2 -2
  37. package/dist/callbacks/invalidateQueries.d.ts.map +1 -1
  38. package/dist/callbacks/invalidateQueries.js.map +1 -1
  39. package/dist/callbacks/isFetching.d.ts +2 -2
  40. package/dist/callbacks/isFetching.d.ts.map +1 -1
  41. package/dist/callbacks/isFetching.js.map +1 -1
  42. package/dist/callbacks/mutationFn.d.ts +2 -2
  43. package/dist/callbacks/mutationFn.d.ts.map +1 -1
  44. package/dist/callbacks/mutationFn.js.map +1 -1
  45. package/dist/callbacks/prefetchInfiniteQuery.d.ts +7 -0
  46. package/dist/callbacks/prefetchInfiniteQuery.d.ts.map +1 -0
  47. package/dist/callbacks/prefetchInfiniteQuery.js +5 -0
  48. package/dist/callbacks/prefetchInfiniteQuery.js.map +1 -0
  49. package/dist/callbacks/prefetchQuery.d.ts +7 -0
  50. package/dist/callbacks/prefetchQuery.d.ts.map +1 -0
  51. package/dist/callbacks/prefetchQuery.js +5 -0
  52. package/dist/callbacks/prefetchQuery.js.map +1 -0
  53. package/dist/callbacks/queryFn.js.map +1 -1
  54. package/dist/callbacks/refetchQueries.d.ts +3 -3
  55. package/dist/callbacks/refetchQueries.d.ts.map +1 -1
  56. package/dist/callbacks/refetchQueries.js.map +1 -1
  57. package/dist/callbacks/resetQueries.d.ts +2 -2
  58. package/dist/callbacks/resetQueries.d.ts.map +1 -1
  59. package/dist/callbacks/resetQueries.js.map +1 -1
  60. package/dist/callbacks/setInfiniteQueryData.d.ts +2 -2
  61. package/dist/callbacks/setInfiniteQueryData.d.ts.map +1 -1
  62. package/dist/callbacks/setInfiniteQueryData.js +2 -6
  63. package/dist/callbacks/setInfiniteQueryData.js.map +1 -1
  64. package/dist/callbacks/setQueryData.d.ts +3 -3
  65. package/dist/callbacks/setQueryData.d.ts.map +1 -1
  66. package/dist/callbacks/setQueryData.js +3 -7
  67. package/dist/callbacks/setQueryData.js.map +1 -1
  68. package/dist/callbacks/useInfiniteQuery.d.ts +4 -4
  69. package/dist/callbacks/useInfiniteQuery.d.ts.map +1 -1
  70. package/dist/callbacks/useInfiniteQuery.js +5 -24
  71. package/dist/callbacks/useInfiniteQuery.js.map +1 -1
  72. package/dist/callbacks/useIsFetching.d.ts +2 -2
  73. package/dist/callbacks/useIsFetching.d.ts.map +1 -1
  74. package/dist/callbacks/useIsFetching.js +3 -8
  75. package/dist/callbacks/useIsFetching.js.map +1 -1
  76. package/dist/callbacks/useMutation.d.ts +2 -2
  77. package/dist/callbacks/useMutation.d.ts.map +1 -1
  78. package/dist/callbacks/useMutation.js +11 -8
  79. package/dist/callbacks/useMutation.js.map +1 -1
  80. package/dist/callbacks/useMutationState.d.ts +2 -2
  81. package/dist/callbacks/useMutationState.d.ts.map +1 -1
  82. package/dist/callbacks/useMutationState.js +3 -7
  83. package/dist/callbacks/useMutationState.js.map +1 -1
  84. package/dist/callbacks/useQueries.d.ts +2 -2
  85. package/dist/callbacks/useQueries.d.ts.map +1 -1
  86. package/dist/callbacks/useQueries.js +9 -6
  87. package/dist/callbacks/useQueries.js.map +1 -1
  88. package/dist/callbacks/useQuery.d.ts +4 -4
  89. package/dist/callbacks/useQuery.d.ts.map +1 -1
  90. package/dist/callbacks/useQuery.js +5 -23
  91. package/dist/callbacks/useQuery.js.map +1 -1
  92. package/dist/callbacks/useSuspenseInfiniteQuery.d.ts +4 -4
  93. package/dist/callbacks/useSuspenseInfiniteQuery.d.ts.map +1 -1
  94. package/dist/callbacks/useSuspenseInfiniteQuery.js +5 -24
  95. package/dist/callbacks/useSuspenseInfiniteQuery.js.map +1 -1
  96. package/dist/callbacks/useSuspenseQueries.d.ts +2 -2
  97. package/dist/callbacks/useSuspenseQueries.d.ts.map +1 -1
  98. package/dist/callbacks/useSuspenseQueries.js +9 -6
  99. package/dist/callbacks/useSuspenseQueries.js.map +1 -1
  100. package/dist/callbacks/useSuspenseQuery.d.ts +4 -4
  101. package/dist/callbacks/useSuspenseQuery.d.ts.map +1 -1
  102. package/dist/callbacks/useSuspenseQuery.js +5 -23
  103. package/dist/callbacks/useSuspenseQuery.js.map +1 -1
  104. package/dist/index.d.ts +3 -7
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +1 -1
  107. package/dist/index.js.map +1 -1
  108. package/dist/lib/callQueryClientFetchMethod.d.ts +20 -0
  109. package/dist/lib/callQueryClientFetchMethod.d.ts.map +1 -0
  110. package/dist/lib/callQueryClientFetchMethod.js +48 -0
  111. package/dist/lib/callQueryClientFetchMethod.js.map +1 -0
  112. package/dist/lib/callQueryClientMethodWithQueryFilters.d.ts +2 -2
  113. package/dist/lib/callQueryClientMethodWithQueryFilters.d.ts.map +1 -1
  114. package/dist/lib/callQueryClientMethodWithQueryFilters.js +5 -13
  115. package/dist/lib/callQueryClientMethodWithQueryFilters.js.map +1 -1
  116. package/dist/lib/callQueryClientMethodWithQueryKey.d.ts +11 -0
  117. package/dist/lib/callQueryClientMethodWithQueryKey.d.ts.map +1 -0
  118. package/dist/lib/callQueryClientMethodWithQueryKey.js +22 -0
  119. package/dist/lib/callQueryClientMethodWithQueryKey.js.map +1 -0
  120. package/dist/lib/composeInfiniteQueryKey.d.ts +3 -3
  121. package/dist/lib/composeInfiniteQueryKey.d.ts.map +1 -1
  122. package/dist/lib/composeMutationKey.d.ts +4 -4
  123. package/dist/lib/composeMutationKey.d.ts.map +1 -1
  124. package/dist/lib/composeMutationKey.js.map +1 -1
  125. package/dist/lib/composeQueryFilters.d.ts +3 -3
  126. package/dist/lib/composeQueryFilters.d.ts.map +1 -1
  127. package/dist/lib/composeQueryFilters.js.map +1 -1
  128. package/dist/lib/composeQueryKey.d.ts +3 -3
  129. package/dist/lib/composeQueryKey.d.ts.map +1 -1
  130. package/dist/lib/requestFn.d.ts +75 -0
  131. package/dist/lib/requestFn.d.ts.map +1 -0
  132. package/dist/lib/{request.js → requestFn.js} +40 -34
  133. package/dist/lib/requestFn.js.map +1 -0
  134. package/dist/lib/useComposeUseQueryOptions.d.ts +16 -0
  135. package/dist/lib/useComposeUseQueryOptions.d.ts.map +1 -0
  136. package/dist/lib/useComposeUseQueryOptions.js +40 -0
  137. package/dist/lib/useComposeUseQueryOptions.js.map +1 -0
  138. package/dist/lib/useQueryClient.d.ts +9 -0
  139. package/dist/lib/useQueryClient.d.ts.map +1 -0
  140. package/dist/lib/useQueryClient.js +15 -0
  141. package/dist/lib/useQueryClient.js.map +1 -0
  142. package/dist/qraftAPIClient.d.ts +7 -7
  143. package/dist/qraftAPIClient.d.ts.map +1 -1
  144. package/package.json +2 -2
  145. package/src/QraftContext.ts +22 -10
  146. package/src/ServiceOperation.ts +261 -4
  147. package/src/callbacks/cancelQueries.ts +3 -3
  148. package/src/callbacks/fetchInfiniteQuery.ts +22 -0
  149. package/src/callbacks/fetchQuery.ts +20 -0
  150. package/src/callbacks/getInfiniteQueryData.ts +13 -16
  151. package/src/callbacks/getInfiniteQueryKey.ts +3 -3
  152. package/src/callbacks/getMutationKey.ts +3 -3
  153. package/src/callbacks/getQueryData.ts +12 -10
  154. package/src/callbacks/getQueryKey.ts +3 -3
  155. package/src/callbacks/invalidateQueries.ts +3 -3
  156. package/src/callbacks/isFetching.ts +3 -3
  157. package/src/callbacks/mutationFn.ts +3 -3
  158. package/src/callbacks/prefetchInfiniteQuery.ts +22 -0
  159. package/src/callbacks/prefetchQuery.ts +22 -0
  160. package/src/callbacks/queryFn.ts +2 -2
  161. package/src/callbacks/refetchQueries.ts +4 -4
  162. package/src/callbacks/resetQueries.ts +3 -3
  163. package/src/callbacks/setInfiniteQueryData.ts +11 -18
  164. package/src/callbacks/setQueryData.ts +16 -17
  165. package/src/callbacks/useInfiniteQuery.ts +8 -46
  166. package/src/callbacks/useIsFetching.ts +6 -20
  167. package/src/callbacks/useMutation.ts +13 -12
  168. package/src/callbacks/useMutationState.ts +5 -11
  169. package/src/callbacks/useQueries.ts +11 -10
  170. package/src/callbacks/useQuery.ts +8 -39
  171. package/src/callbacks/useSuspenseInfiniteQuery.ts +8 -42
  172. package/src/callbacks/useSuspenseQueries.ts +11 -10
  173. package/src/callbacks/useSuspenseQuery.ts +8 -43
  174. package/src/index.ts +12 -7
  175. package/src/lib/callQueryClientFetchMethod.ts +90 -0
  176. package/src/lib/callQueryClientMethodWithQueryFilters.ts +10 -23
  177. package/src/lib/callQueryClientMethodWithQueryKey.ts +46 -0
  178. package/src/lib/composeInfiniteQueryKey.ts +3 -3
  179. package/src/lib/composeMutationKey.ts +5 -8
  180. package/src/lib/composeQueryFilters.ts +2 -2
  181. package/src/lib/composeQueryKey.ts +3 -3
  182. package/src/lib/{request.ts → requestFn.ts} +143 -79
  183. package/src/lib/useComposeUseQueryOptions.ts +64 -0
  184. package/src/lib/useQueryClient.ts +26 -0
  185. package/src/qraftAPIClient.ts +8 -8
  186. package/dist/RequestClient.d.ts +0 -19
  187. package/dist/RequestClient.d.ts.map +0 -1
  188. package/dist/RequestClient.js +0 -2
  189. package/dist/RequestClient.js.map +0 -1
  190. package/dist/lib/request.d.ts +0 -32
  191. package/dist/lib/request.d.ts.map +0 -1
  192. package/dist/lib/request.js.map +0 -1
  193. package/src/RequestClient.ts +0 -32
package/src/index.ts CHANGED
@@ -1,11 +1,16 @@
1
- export { qraftAPIClient } from './qraftAPIClient.js';
2
- export type { QraftClientOptions } from './qraftAPIClient.js';
3
- export { QraftContext } from './QraftContext.js';
4
- export type { QraftContextValue } from './QraftContext.js';
5
- export { request, urlSerializer, bodySerializer } from './lib/request.js';
6
- export type { APIRequestInit, HeadersOptions } from './lib/request.js';
1
+ export { qraftAPIClient, type QraftClientOptions } from './qraftAPIClient.js';
2
+ export { QraftContext, type QraftContextValue } from './QraftContext.js';
3
+ export {
4
+ requestFn,
5
+ baseRequestFn,
6
+ urlSerializer,
7
+ bodySerializer,
8
+ type RequestFnPayload,
9
+ type OperationSchema,
10
+ type RequestFnOptions,
11
+ type HeadersOptions,
12
+ } from './lib/requestFn.js';
7
13
  export type {
8
14
  ServiceOperationQuery,
9
15
  ServiceOperationMutation,
10
16
  } from './ServiceOperation.js';
11
- export type { RequestClient } from './RequestClient.js';
@@ -0,0 +1,90 @@
1
+ import type { QueryClient } from '@tanstack/query-core';
2
+
3
+ import { composeInfiniteQueryKey } from './composeInfiniteQueryKey.js';
4
+ import { composeQueryKey } from './composeQueryKey.js';
5
+ import type { OperationSchema, RequestFn } from './requestFn.js';
6
+ import { shelfMerge } from './shelfMerge.js';
7
+
8
+ /**
9
+ * Calls a query client fetch method with parameters and options,
10
+ * and automatically composes the `QueryKey` based on the schema and parameters.
11
+ */
12
+ export function callQueryClientMethodWithQueryKey<
13
+ QFMethod extends QueryKeyMethods,
14
+ >(
15
+ queryFilterMethod: QFMethod,
16
+ schema: OperationSchema,
17
+ infinite: boolean,
18
+ args: QueryClientMethodArgs<QFMethod>
19
+ ): ReturnType<(typeof QueryClient.prototype)[QFMethod]> {
20
+ const [{ requestFn, baseUrl, parameters, ...options }, queryClient] = args;
21
+
22
+ if (!queryClient) throw new Error('queryClient is required');
23
+ if (!queryClient[queryFilterMethod])
24
+ throw new Error(
25
+ `queryClient is invalid, ${queryFilterMethod} method does not exist`
26
+ );
27
+
28
+ if (options.queryKey && parameters) {
29
+ throw new Error(
30
+ 'callQueryClientMethodWithQueryKey: options.queryKey and options.parameters are mutually exclusive'
31
+ );
32
+ }
33
+
34
+ if (options.queryFn && requestFn) {
35
+ throw new Error(
36
+ 'callQueryClientMethodWithQueryKey: options.queryFn and requestFn are mutually exclusive'
37
+ );
38
+ }
39
+
40
+ const queryFn =
41
+ options.queryFn ??
42
+ (requestFn
43
+ ? // @ts-expect-error
44
+ function ({ queryKey: [, queryParams], signal, meta, pageParam }) {
45
+ return requestFn(schema, {
46
+ parameters: infinite
47
+ ? (shelfMerge(2, queryParams, pageParam) as never)
48
+ : queryParams,
49
+ baseUrl,
50
+ signal,
51
+ meta,
52
+ });
53
+ }
54
+ : undefined);
55
+
56
+ if (parameters) {
57
+ // @ts-expect-error
58
+ return queryClient[queryFilterMethod]({
59
+ ...options,
60
+ // Event if queryFn is undefined, it will override the default queryFn from `setQueryDefaults(...)`
61
+ ...(queryFn ? { queryFn } : {}),
62
+ queryKey: infinite
63
+ ? composeInfiniteQueryKey(schema, parameters)
64
+ : composeQueryKey(schema, parameters),
65
+ });
66
+ }
67
+
68
+ // @ts-expect-error
69
+ return queryClient[queryFilterMethod]({ ...options, queryFn });
70
+ }
71
+
72
+ type QueryClientMethodArgs<QMethod extends keyof QueryClientPrototype> = [
73
+ options: Parameters<QueryClientPrototype[QMethod]>[0] & {
74
+ parameters?: unknown;
75
+ baseUrl?: string;
76
+ requestFn?: RequestFn<unknown>;
77
+ },
78
+ queryClient: QueryClient,
79
+ ];
80
+
81
+ type QueryKeyMethods =
82
+ | QueryKeyMethod<'fetchQuery'>
83
+ | QueryKeyMethod<'prefetchQuery'>
84
+ | QueryKeyMethod<'fetchInfiniteQuery'>
85
+ | QueryKeyMethod<'prefetchInfiniteQuery'>;
86
+
87
+ type QueryClientPrototype = typeof QueryClient.prototype;
88
+
89
+ type QueryKeyMethod<QFMethod extends keyof typeof QueryClient.prototype> =
90
+ QFMethod;
@@ -1,7 +1,7 @@
1
1
  import type { QueryClient } from '@tanstack/query-core';
2
2
 
3
- import type { RequestClientSchema } from '../RequestClient.js';
4
3
  import { composeQueryFilters } from './composeQueryFilters.js';
4
+ import type { OperationSchema } from './requestFn.js';
5
5
 
6
6
  /**
7
7
  * Calls a query client method with query filters and options,
@@ -11,24 +11,11 @@ export function callQueryClientMethodWithQueryFilters<
11
11
  QFMethod extends QueryFilterMethods,
12
12
  >(
13
13
  queryFilterMethod: QFMethod,
14
- schema: RequestClientSchema,
15
- args: [QueryClient, ...Parameters<(typeof QueryClient.prototype)[QFMethod]>]
14
+ schema: OperationSchema,
15
+ args: [...Parameters<(typeof QueryClient.prototype)[QFMethod]>, QueryClient]
16
16
  ): ReturnType<(typeof QueryClient.prototype)[QFMethod]> {
17
- const queryClient = (
18
- args.length === 1
19
- ? args[0]
20
- : args.length === 2
21
- ? args[1]
22
- : args.length === 3
23
- ? args[2]
24
- : undefined
25
- ) as QueryClient | undefined;
26
-
27
- const filters = composeQueryFilters(
28
- schema,
29
- args.length === 1 ? undefined : args[0]
30
- );
31
- const options = args.length === 3 ? args[1] : undefined;
17
+ const filters = args[0];
18
+ const queryClient = args[args.length - 1] as QueryClient | undefined;
32
19
 
33
20
  if (!queryClient) throw new Error('queryClient is required');
34
21
  if (!queryClient[queryFilterMethod])
@@ -36,12 +23,12 @@ export function callQueryClientMethodWithQueryFilters<
36
23
  `queryClient is invalid, ${queryFilterMethod} method does not exist`
37
24
  );
38
25
 
39
- if (options)
40
- // @ts-expect-error
41
- return queryClient[queryFilterMethod](filters, options);
42
-
43
26
  // @ts-expect-error
44
- return queryClient[queryFilterMethod](filters);
27
+ return queryClient[queryFilterMethod](
28
+ composeQueryFilters(schema, filters as never),
29
+ // @ts-expect-error
30
+ ...args.slice(1, -1)
31
+ );
45
32
  }
46
33
 
47
34
  type QueryFiltersMethod<QFMethod extends keyof typeof QueryClient.prototype> =
@@ -0,0 +1,46 @@
1
+ import type { QueryClient } from '@tanstack/query-core';
2
+
3
+ import { composeInfiniteQueryKey } from './composeInfiniteQueryKey.js';
4
+ import { composeQueryKey } from './composeQueryKey.js';
5
+ import type { OperationSchema } from './requestFn.js';
6
+
7
+ /**
8
+ * Calls a query client method with parameters and options,
9
+ * and automatically composes the `QueryKey` based on the schema and parameters.
10
+ */
11
+ export function callQueryClientMethodWithQueryKey<
12
+ QFMethod extends QueryKeyMethods,
13
+ >(
14
+ queryFilterMethod: QFMethod,
15
+ schema: OperationSchema,
16
+ infinite: boolean,
17
+ args: [...Parameters<(typeof QueryClient.prototype)[QFMethod]>, QueryClient]
18
+ ): ReturnType<(typeof QueryClient.prototype)[QFMethod]> {
19
+ const parameters = args[0];
20
+ const queryClient = args[args.length - 1] as QueryClient | undefined;
21
+
22
+ if (!queryClient) throw new Error('queryClient is required');
23
+ if (!queryClient[queryFilterMethod])
24
+ throw new Error(
25
+ `queryClient is invalid, ${queryFilterMethod} method does not exist`
26
+ );
27
+
28
+ const queryKey = Array.isArray(parameters)
29
+ ? parameters
30
+ : infinite
31
+ ? composeInfiniteQueryKey(schema, parameters)
32
+ : composeQueryKey(schema, parameters);
33
+
34
+ // @ts-expect-error
35
+ return queryClient[queryFilterMethod](queryKey, ...args.slice(1, -1));
36
+ }
37
+
38
+ type QueryKeyMethod<QFMethod extends keyof typeof QueryClient.prototype> =
39
+ QFMethod;
40
+
41
+ type QueryKeyMethods =
42
+ | QueryKeyMethod<'setQueryData'>
43
+ | QueryKeyMethod<'getQueryData'>
44
+ | QueryKeyMethod<'getQueryState'>
45
+ | QueryKeyMethod<'setQueryDefaults'>
46
+ | QueryKeyMethod<'getQueryDefaults'>;
@@ -1,8 +1,8 @@
1
- import type { RequestClientSchema } from '../RequestClient.js';
2
- import { ServiceOperationInfiniteQueryKey } from '../ServiceOperation.js';
1
+ import type { ServiceOperationInfiniteQueryKey } from '../ServiceOperation.js';
2
+ import type { OperationSchema } from './requestFn.js';
3
3
 
4
4
  export function composeInfiniteQueryKey<
5
- TSchema extends RequestClientSchema,
5
+ TSchema extends OperationSchema,
6
6
  TParams,
7
7
  >(
8
8
  schema: TSchema,
@@ -1,26 +1,23 @@
1
- import type { RequestClientSchema } from '../RequestClient.js';
2
- import { ServiceOperationMutationKey } from '../ServiceOperation.js';
1
+ import type { ServiceOperationMutationKey } from '../ServiceOperation.js';
2
+ import type { OperationSchema } from './requestFn.js';
3
3
 
4
4
  /**
5
5
  * Omit `body` or `requestBody` from mutation parameters if exists
6
6
  * and return the rest of the parameters
7
7
  */
8
8
  export function composeMutationKey<
9
- TSchema extends RequestClientSchema,
9
+ TSchema extends OperationSchema,
10
10
  TParams = undefined,
11
11
  >(
12
12
  schema: TSchema,
13
13
  parameters: undefined
14
14
  ): ServiceOperationMutationKey<TSchema, undefined>;
15
- export function composeMutationKey<
16
- TSchema extends RequestClientSchema,
17
- TParams,
18
- >(
15
+ export function composeMutationKey<TSchema extends OperationSchema, TParams>(
19
16
  schema: TSchema,
20
17
  parameters: TParams
21
18
  ): ServiceOperationMutationKey<TSchema, TParams>;
22
19
  export function composeMutationKey<
23
- TSchema extends RequestClientSchema,
20
+ TSchema extends OperationSchema,
24
21
  TParams = unknown,
25
22
  >(schema: TSchema, parameters: TParams | undefined) {
26
23
  return parameters === undefined
@@ -1,5 +1,5 @@
1
- import type { RequestClientSchema } from '../RequestClient.js';
2
1
  import { composeQueryKey } from './composeQueryKey.js';
2
+ import type { OperationSchema } from './requestFn.js';
3
3
 
4
4
  /**
5
5
  * Replaces the `parameters` field in the filters with a `queryKey` field based on the schema.
@@ -8,7 +8,7 @@ import { composeQueryKey } from './composeQueryKey.js';
8
8
  * @param filters
9
9
  */
10
10
  export function composeQueryFilters<Filters extends object>(
11
- schema: RequestClientSchema,
11
+ schema: OperationSchema,
12
12
  filters: Filters | undefined
13
13
  ) {
14
14
  if (!filters) {
@@ -1,7 +1,7 @@
1
- import type { RequestClientSchema } from '../RequestClient.js';
2
- import { ServiceOperationQueryKey } from '../ServiceOperation.js';
1
+ import type { ServiceOperationQueryKey } from '../ServiceOperation.js';
2
+ import type { OperationSchema } from './requestFn.js';
3
3
 
4
- export function composeQueryKey<TSchema extends RequestClientSchema, TParams>(
4
+ export function composeQueryKey<TSchema extends OperationSchema, TParams>(
5
5
  schema: TSchema,
6
6
  parameters: TParams | undefined
7
7
  ): ServiceOperationQueryKey<TSchema, TParams> {
@@ -1,50 +1,52 @@
1
1
  /**
2
2
  * @throws {error: object|string, response: Response} if the request fails
3
3
  */
4
- export async function request<T>(
5
- config: { baseUrl: string },
6
- requestInit: APIRequestInit,
7
- {
8
- urlSerializer: urlSerializerFunc,
9
- bodySerializer: bodySerializerFunc,
10
- }: {
11
- urlSerializer: typeof urlSerializer;
12
- bodySerializer: typeof bodySerializer;
13
- }
4
+ export async function requestFn<T>(
5
+ schema: OperationSchema,
6
+ requestInfo: RequestFnInfo,
7
+ options?: RequestFnOptions
14
8
  ): Promise<T> {
15
- const {
16
- method,
17
- url,
18
- parameters,
19
- mediaType,
20
- headers,
21
- body,
22
- ...requestInitRest
23
- } = requestInit;
24
-
25
- const requestBody = bodySerializerFunc({
26
- method,
27
- mediaType,
28
- body,
9
+ return baseRequestFn(schema, requestInfo, {
10
+ urlSerializer,
11
+ bodySerializer,
12
+ ...options,
29
13
  });
14
+ }
30
15
 
31
- const response = await fetch(urlSerializerFunc(config, { url, parameters }), {
32
- method: method.toUpperCase(),
33
- body: requestBody,
34
- headers: mergeHeaders(
35
- {
36
- Accept: 'application/json',
37
- 'Content-Type': mediaType ?? getBodyContentType(body),
38
- },
39
- headers,
40
- parameters?.header,
41
- requestBody instanceof FormData
42
- ? // remove `Content-Type` if serialized body is FormData; browser will correctly set Content-Type & boundary expression
43
- { 'Content-Type': null }
44
- : undefined
45
- ),
46
- ...requestInitRest,
47
- });
16
+ /**
17
+ * @throws {error: object|string, response: Response} if the request fails
18
+ */
19
+ export async function baseRequestFn<T>(
20
+ requestSchema: OperationSchema,
21
+ requestInfo: RequestFnInfo,
22
+ options: WithRequired<RequestFnOptions, 'urlSerializer' | 'bodySerializer'>
23
+ ): Promise<T> {
24
+ const { parameters, headers, body, ...requestInfoRest } = requestInfo;
25
+
26
+ const requestBody = options.bodySerializer(requestSchema, requestInfo);
27
+
28
+ const baseFetch = options.fetch ?? fetch;
29
+
30
+ const response = await baseFetch(
31
+ options.urlSerializer(requestSchema, requestInfo),
32
+ {
33
+ method: requestSchema.method.toUpperCase(),
34
+ body: requestBody,
35
+ headers: mergeHeaders(
36
+ {
37
+ Accept: 'application/json',
38
+ 'Content-Type': requestSchema.mediaType ?? getBodyContentType(body),
39
+ },
40
+ headers,
41
+ parameters?.header,
42
+ requestBody instanceof FormData
43
+ ? // remove `Content-Type` if serialized body is FormData; browser will correctly set Content-Type & boundary expression
44
+ { 'Content-Type': null }
45
+ : undefined
46
+ ),
47
+ ...requestInfoRest,
48
+ }
49
+ );
48
50
 
49
51
  // clone response to allow multiple reads
50
52
  const clonedResponse = response.clone();
@@ -57,21 +59,26 @@ export async function request<T>(
57
59
  }
58
60
 
59
61
  export function urlSerializer(
60
- config: { baseUrl: string },
61
- { url, parameters }: Pick<APIRequestInit, 'url' | 'parameters'>
62
+ schema: OperationSchema,
63
+ info: RequestFnPayload
62
64
  ): string {
63
- const path = url.replace(/{(.*?)}/g, (substring: string, group: string) => {
64
- if (parameters?.path?.hasOwnProperty(group)) {
65
- return encodeURI(String(parameters?.path[group]));
65
+ const path = schema.url.replace(
66
+ /{(.*?)}/g,
67
+ (substring: string, group: string) => {
68
+ if (info.parameters?.path?.hasOwnProperty(group)) {
69
+ return encodeURI(String(info.parameters?.path[group]));
70
+ }
71
+ return substring;
66
72
  }
67
- return substring;
68
- });
73
+ );
69
74
 
70
- if (parameters?.query) {
71
- return `${config.baseUrl}${path}${getQueryString(parameters.query)}`;
75
+ const baseUrl = info.baseUrl ?? '';
76
+
77
+ if (info.parameters?.query) {
78
+ return `${baseUrl}${path}${getQueryString(info.parameters.query)}`;
72
79
  }
73
80
 
74
- return `${config.baseUrl}${path}`;
81
+ return `${baseUrl}${path}`;
75
82
  }
76
83
 
77
84
  function getQueryString(params: Record<string, any>): string {
@@ -133,38 +140,37 @@ function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]) {
133
140
  return headers;
134
141
  }
135
142
 
136
- export function bodySerializer(options: {
137
- method: APIRequestInit['method'];
138
- mediaType: APIRequestInit['mediaType'];
139
- body: APIRequestInit['body'];
140
- }) {
141
- if (options.body === undefined) return;
143
+ export function bodySerializer(
144
+ schema: OperationSchema,
145
+ info: RequestFnPayload
146
+ ) {
147
+ if (info.body === undefined) return;
142
148
 
143
149
  if (
144
- options.method === 'get' ||
145
- options.method === 'head' ||
146
- options.method === 'options'
150
+ schema.method === 'get' ||
151
+ schema.method === 'head' ||
152
+ schema.method === 'options'
147
153
  )
148
154
  return;
149
155
 
150
- if (options.mediaType?.includes('/form-data'))
151
- return getRequestBodyFormData(options);
156
+ if (schema.mediaType?.includes('/form-data'))
157
+ return getRequestBodyFormData(info);
152
158
 
153
159
  if (
154
- !options.mediaType?.includes('/json') &&
155
- (typeof options.body === 'string' ||
156
- options.body instanceof Blob ||
157
- options.body instanceof FormData)
160
+ !schema.mediaType?.includes('/json') &&
161
+ (typeof info.body === 'string' ||
162
+ info.body instanceof Blob ||
163
+ info.body instanceof FormData)
158
164
  ) {
159
- return options.body;
165
+ return info.body;
160
166
  }
161
167
 
162
- return JSON.stringify(options.body);
168
+ return JSON.stringify(info.body);
163
169
  }
164
170
 
165
171
  function getRequestBodyFormData({
166
172
  body,
167
- }: Pick<APIRequestInit, 'body'>): FormData | undefined {
173
+ }: Pick<RequestFnPayload, 'body'>): FormData | undefined {
168
174
  if (body instanceof FormData) return body;
169
175
  if (body === null) return;
170
176
 
@@ -203,7 +209,7 @@ function getRequestBodyFormData({
203
209
  return formData;
204
210
  }
205
211
 
206
- function getBodyContentType(body: APIRequestInit['body']) {
212
+ function getBodyContentType(body: RequestFnPayload['body']) {
207
213
  if (!body) return;
208
214
  if (body instanceof Blob) return body.type || 'application/octet-stream';
209
215
  if (typeof body === 'string') return 'text/plain';
@@ -225,11 +231,7 @@ async function getResponseBody<T>(response: Response): Promise<T | undefined> {
225
231
  const errorFallbackResponse = response.clone(); // clone to allow multiple reads
226
232
 
227
233
  return response.json().catch(() => {
228
- if (
229
- typeof process !== 'undefined' &&
230
- typeof process.env !== 'undefined' &&
231
- process.env.NODE_ENV === 'development'
232
- ) {
234
+ if (process.env.NODE_ENV === 'development') {
233
235
  console.warn(
234
236
  'Failed to parse response body as JSON. Falling back to .text()'
235
237
  );
@@ -247,7 +249,16 @@ export type HeadersOptions =
247
249
  | HeadersInit
248
250
  | Record<string, string | number | boolean | null | undefined>;
249
251
 
250
- export type APIRequestInit = {
252
+ export interface OperationSchema {
253
+ /**
254
+ * Operation path
255
+ * @example /user/{id}
256
+ */
257
+ readonly url: string;
258
+
259
+ /**
260
+ * Operation method
261
+ */
251
262
  readonly method:
252
263
  | 'get'
253
264
  | 'put'
@@ -257,14 +268,67 @@ export type APIRequestInit = {
257
268
  | 'options'
258
269
  | 'head'
259
270
  | 'trace';
260
- readonly url: string;
271
+
272
+ /**
273
+ * Media type of the request body
274
+ * @example application/json
275
+ */
276
+ readonly mediaType?: string;
277
+ }
278
+
279
+ export interface RequestFnPayload {
280
+ /**
281
+ * Base URL to use for the request
282
+ */
283
+ baseUrl: string | undefined;
284
+
285
+ /**
286
+ * OpenAPI parameters
287
+ * @example
288
+ * ```ts
289
+ * { path: {id: 1}, query: {search: 'hello'} }
290
+ * ```
291
+ */
261
292
  readonly parameters?: {
262
293
  readonly path?: Record<string, any>;
263
294
  readonly cookie?: Record<string, any>;
264
295
  readonly header?: Record<string, any>;
265
296
  readonly query?: Record<string, any>;
266
297
  };
298
+
299
+ /**
300
+ * Request body
301
+ * @example { name: 'John' }
302
+ */
267
303
  readonly body?: BodyInit | Record<string, unknown> | null;
268
- readonly mediaType?: string;
304
+
305
+ /**
306
+ * Tanstack Query Meta
307
+ */
308
+ meta?: Record<string, unknown>;
309
+
310
+ /** An AbortSignal to set request's signal. */
311
+ signal?: AbortSignal | null;
312
+ }
313
+
314
+ interface RequestFnInfo
315
+ extends RequestFnPayload,
316
+ Omit<RequestInit, 'headers' | 'method' | 'body' | 'signal'> {
317
+ /**
318
+ * Request headers
319
+ * @example { 'X-Auth': '123' }
320
+ */
269
321
  readonly headers?: HeadersOptions;
270
- } & Omit<RequestInit, 'headers' | 'method' | 'body'>;
322
+ }
323
+
324
+ export interface RequestFnOptions {
325
+ urlSerializer?: typeof urlSerializer;
326
+ bodySerializer?: typeof bodySerializer;
327
+ fetch?: typeof fetch;
328
+ }
329
+
330
+ export type RequestFn<T> = typeof requestFn<T>;
331
+
332
+ type WithRequired<T, K extends keyof T> = T & {
333
+ [_ in K]: {};
334
+ };
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+
5
+ import type { QueryClient } from '@tanstack/query-core';
6
+ import type { UseQueryOptions } from '@tanstack/react-query';
7
+
8
+ import type { QraftClientOptions } from '../qraftAPIClient.js';
9
+ import { QraftContext } from '../QraftContext.js';
10
+ import type { ServiceOperationQueryKey } from '../ServiceOperation.js';
11
+ import { composeInfiniteQueryKey } from './composeInfiniteQueryKey.js';
12
+ import { composeQueryKey } from './composeQueryKey.js';
13
+ import type { OperationSchema } from './requestFn.js';
14
+ import { shelfMerge } from './shelfMerge.js';
15
+ import { useQueryClient } from './useQueryClient.js';
16
+
17
+ /**
18
+ * Composes the options for useQuery, useInfiniteQuery, useSuspenseQuery, and useSuspenseQueries.
19
+ * @internal
20
+ */
21
+ export function useComposeUseQueryOptions(
22
+ qraftOptions: QraftClientOptions | undefined,
23
+ schema: OperationSchema,
24
+ args: UseQueryOptionsArgs,
25
+ infinite: boolean
26
+ ): never {
27
+ const [parameters, options, queryClient] = args;
28
+
29
+ const contextValue = useContext(qraftOptions?.context ?? QraftContext);
30
+ if (!contextValue?.requestFn)
31
+ throw new Error(`QraftContext.requestFn not found`);
32
+
33
+ const queryFn =
34
+ options?.queryFn ??
35
+ // @ts-expect-error
36
+ function ({ queryKey: [, queryParams], signal, meta, pageParam }) {
37
+ return contextValue.requestFn(schema, {
38
+ // @ts-expect-error
39
+ parameters: infinite
40
+ ? (shelfMerge(2, queryParams, pageParam) as never)
41
+ : queryParams,
42
+ baseUrl: contextValue.baseUrl,
43
+ signal,
44
+ meta,
45
+ });
46
+ };
47
+
48
+ const queryKey = Array.isArray(parameters)
49
+ ? (parameters as ServiceOperationQueryKey<OperationSchema, unknown>)
50
+ : infinite
51
+ ? composeInfiniteQueryKey(schema, parameters)
52
+ : composeQueryKey(schema, parameters);
53
+
54
+ return [
55
+ { ...options, queryFn, queryKey },
56
+ useQueryClient(qraftOptions, queryClient),
57
+ ] as never;
58
+ }
59
+
60
+ type UseQueryOptionsArgs = [
61
+ parameters: unknown,
62
+ options?: UseQueryOptions,
63
+ queryClient?: QueryClient,
64
+ ];
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+
5
+ import {
6
+ QueryClient,
7
+ useQueryClient as useQueryClientTanstack,
8
+ } from '@tanstack/react-query';
9
+
10
+ import type { QraftClientOptions } from '../qraftAPIClient.js';
11
+ import { QraftContext } from '../QraftContext.js';
12
+
13
+ /**
14
+ * Get the QueryClient from the QraftContext or the provided QueryClient argument.
15
+ * @param qraftOptions if `queryClient` is not provided, will be taken from context
16
+ * @param queryClient If not provided, will be taken this instance
17
+ */
18
+ export function useQueryClient(
19
+ qraftOptions: QraftClientOptions | undefined,
20
+ queryClient: QueryClient | undefined
21
+ ) {
22
+ const queryClientByContext = useContext(qraftOptions?.context ?? QraftContext)
23
+ ?.queryClient;
24
+
25
+ return useQueryClientTanstack(queryClient ?? queryClientByContext);
26
+ }