@openapi-qraft/react 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -0
- package/dist/QraftContext.d.ts +8 -0
- package/dist/QraftContext.d.ts.map +1 -0
- package/dist/QraftContext.js +3 -0
- package/dist/QraftContext.js.map +1 -0
- package/dist/RequestClient.d.ts +19 -0
- package/dist/RequestClient.d.ts.map +1 -0
- package/dist/RequestClient.js +2 -0
- package/dist/RequestClient.js.map +1 -0
- package/dist/ServiceOperation.d.ts +122 -0
- package/dist/ServiceOperation.d.ts.map +1 -0
- package/dist/ServiceOperation.js +2 -0
- package/dist/ServiceOperation.js.map +1 -0
- package/dist/callbacks/getInfiniteQueryData.d.ts +6 -0
- package/dist/callbacks/getInfiniteQueryData.d.ts.map +1 -0
- package/dist/callbacks/getInfiniteQueryData.js +8 -0
- package/dist/callbacks/getInfiniteQueryData.js.map +1 -0
- package/dist/callbacks/getInfiniteQueryKey.d.ts +8 -0
- package/dist/callbacks/getInfiniteQueryKey.d.ts.map +1 -0
- package/dist/callbacks/getInfiniteQueryKey.js +7 -0
- package/dist/callbacks/getInfiniteQueryKey.js.map +1 -0
- package/dist/callbacks/getMutationKey.d.ts +5 -0
- package/dist/callbacks/getMutationKey.d.ts.map +1 -0
- package/dist/callbacks/getMutationKey.js +22 -0
- package/dist/callbacks/getMutationKey.js.map +1 -0
- package/dist/callbacks/getQueryData.d.ts +5 -0
- package/dist/callbacks/getQueryData.d.ts.map +1 -0
- package/dist/callbacks/getQueryData.js +8 -0
- package/dist/callbacks/getQueryData.js.map +1 -0
- package/dist/callbacks/getQueryKey.d.ts +7 -0
- package/dist/callbacks/getQueryKey.d.ts.map +1 -0
- package/dist/callbacks/getQueryKey.js +4 -0
- package/dist/callbacks/getQueryKey.js.map +1 -0
- package/dist/callbacks/mutationFn.d.ts +4 -0
- package/dist/callbacks/mutationFn.d.ts.map +1 -0
- package/dist/callbacks/mutationFn.js +8 -0
- package/dist/callbacks/mutationFn.js.map +1 -0
- package/dist/callbacks/queryFn.d.ts +15 -0
- package/dist/callbacks/queryFn.d.ts.map +1 -0
- package/dist/callbacks/queryFn.js +10 -0
- package/dist/callbacks/queryFn.js.map +1 -0
- package/dist/callbacks/setInfiniteQueryData.d.ts +6 -0
- package/dist/callbacks/setInfiniteQueryData.d.ts.map +1 -0
- package/dist/callbacks/setInfiniteQueryData.js +6 -0
- package/dist/callbacks/setInfiniteQueryData.js.map +1 -0
- package/dist/callbacks/setQueryData.d.ts +5 -0
- package/dist/callbacks/setQueryData.d.ts.map +1 -0
- package/dist/callbacks/setQueryData.js +9 -0
- package/dist/callbacks/setQueryData.js.map +1 -0
- package/dist/callbacks/useInfiniteQuery.d.ts +13 -0
- package/dist/callbacks/useInfiniteQuery.d.ts.map +1 -0
- package/dist/callbacks/useInfiniteQuery.js +50 -0
- package/dist/callbacks/useInfiniteQuery.js.map +1 -0
- package/dist/callbacks/useMutation.d.ts +7 -0
- package/dist/callbacks/useMutation.d.ts.map +1 -0
- package/dist/callbacks/useMutation.js +51 -0
- package/dist/callbacks/useMutation.js.map +1 -0
- package/dist/callbacks/useQuery.d.ts +9 -0
- package/dist/callbacks/useQuery.d.ts.map +1 -0
- package/dist/callbacks/useQuery.js +27 -0
- package/dist/callbacks/useQuery.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/createCallbackProxyDecoration.d.ts +5 -0
- package/dist/lib/createCallbackProxyDecoration.d.ts.map +1 -0
- package/dist/lib/createCallbackProxyDecoration.js +21 -0
- package/dist/lib/createCallbackProxyDecoration.js.map +1 -0
- package/dist/lib/createRecursiveProxy.d.ts +7 -0
- package/dist/lib/createRecursiveProxy.d.ts.map +1 -0
- package/dist/lib/createRecursiveProxy.js +24 -0
- package/dist/lib/createRecursiveProxy.js.map +1 -0
- package/dist/lib/request.d.ts +32 -0
- package/dist/lib/request.d.ts.map +1 -0
- package/dist/lib/request.js +167 -0
- package/dist/lib/request.js.map +1 -0
- package/dist/qraftAPIClient.d.ts +25 -0
- package/dist/qraftAPIClient.d.ts.map +1 -0
- package/dist/qraftAPIClient.js +22 -0
- package/dist/qraftAPIClient.js.map +1 -0
- package/package.json +79 -0
- package/src/QraftContext.ts +12 -0
- package/src/RequestClient.ts +32 -0
- package/src/ServiceOperation.ts +306 -0
- package/src/callbacks/getInfiniteQueryData.ts +26 -0
- package/src/callbacks/getInfiniteQueryKey.ts +23 -0
- package/src/callbacks/getMutationKey.ts +47 -0
- package/src/callbacks/getQueryData.ts +20 -0
- package/src/callbacks/getQueryKey.ts +19 -0
- package/src/callbacks/mutationFn.ts +22 -0
- package/src/callbacks/queryFn.ts +21 -0
- package/src/callbacks/setInfiniteQueryData.ts +29 -0
- package/src/callbacks/setQueryData.ts +23 -0
- package/src/callbacks/shelfMerge.test.ts +94 -0
- package/src/callbacks/useInfiniteQuery.ts +88 -0
- package/src/callbacks/useMutation.ts +100 -0
- package/src/callbacks/useQuery.ts +56 -0
- package/src/index.ts +11 -0
- package/src/lib/createCallbackProxyDecoration.ts +34 -0
- package/src/lib/createRecursiveProxy.ts +27 -0
- package/src/lib/request.ts +252 -0
- package/src/mocks/handlers.ts +252 -0
- package/src/mocks/node.ts +5 -0
- package/src/qraftAPIClient.test.tsx +595 -0
- package/src/qraftAPIClient.ts +77 -0
- package/src/setupTests.ts +17 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @throws {error: object|string, response: Response} if the request fails
|
|
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
|
+
}
|
|
14
|
+
): 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,
|
|
29
|
+
});
|
|
30
|
+
|
|
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
|
+
});
|
|
48
|
+
|
|
49
|
+
// clone response to allow multiple reads
|
|
50
|
+
const clonedResponse = response.clone();
|
|
51
|
+
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw await getResponseBody(clonedResponse);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (await getResponseBody(clonedResponse)) as T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function urlSerializer(
|
|
60
|
+
config: { baseUrl: string },
|
|
61
|
+
{ url, parameters }: Pick<APIRequestInit, 'url' | 'parameters'>
|
|
62
|
+
): string {
|
|
63
|
+
const path = url.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
|
64
|
+
if (parameters?.path?.hasOwnProperty(group)) {
|
|
65
|
+
return encodeURI(String(parameters?.path[group]));
|
|
66
|
+
}
|
|
67
|
+
return substring;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (parameters?.query) {
|
|
71
|
+
return `${config.baseUrl}${path}${getQueryString(parameters.query)}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return `${config.baseUrl}${path}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getQueryString(params: Record<string, any>): string {
|
|
78
|
+
const qs: string[] = [];
|
|
79
|
+
|
|
80
|
+
const append = (key: string, value: any) => {
|
|
81
|
+
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const process = (key: string, value: any) => {
|
|
85
|
+
if (typeof value === 'undefined' || value === null) return;
|
|
86
|
+
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
value.forEach((v) => {
|
|
89
|
+
process(key, v);
|
|
90
|
+
});
|
|
91
|
+
} else if (typeof value === 'object') {
|
|
92
|
+
Object.entries(value).forEach(([k, v]) => {
|
|
93
|
+
process(`${key}[${k}]`, v);
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
append(key, value);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
101
|
+
process(key, value);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (qs.length > 0) {
|
|
105
|
+
return `?${qs.join('&')}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]) {
|
|
112
|
+
const headers = new Headers();
|
|
113
|
+
|
|
114
|
+
for (const headerSet of allHeaders) {
|
|
115
|
+
if (!headerSet || typeof headerSet !== 'object') {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const headersIterator =
|
|
120
|
+
headerSet instanceof Headers
|
|
121
|
+
? headerSet.entries()
|
|
122
|
+
: Object.entries(headerSet);
|
|
123
|
+
|
|
124
|
+
for (const [headerKey, headerValue] of headersIterator) {
|
|
125
|
+
if (headerValue === null) {
|
|
126
|
+
headers.delete(headerKey);
|
|
127
|
+
} else if (headerValue !== undefined) {
|
|
128
|
+
headers.set(headerKey, headerValue);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return headers;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function bodySerializer(options: {
|
|
137
|
+
method: APIRequestInit['method'];
|
|
138
|
+
mediaType: APIRequestInit['mediaType'];
|
|
139
|
+
body: APIRequestInit['body'];
|
|
140
|
+
}) {
|
|
141
|
+
if (options.body === undefined) return;
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
options.method === 'get' ||
|
|
145
|
+
options.method === 'head' ||
|
|
146
|
+
options.method === 'options'
|
|
147
|
+
)
|
|
148
|
+
return;
|
|
149
|
+
|
|
150
|
+
if (options.mediaType?.includes('/form-data'))
|
|
151
|
+
return getRequestBodyFormData(options);
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
!options.mediaType?.includes('/json') &&
|
|
155
|
+
(typeof options.body === 'string' ||
|
|
156
|
+
options.body instanceof Blob ||
|
|
157
|
+
options.body instanceof FormData)
|
|
158
|
+
) {
|
|
159
|
+
return options.body;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return JSON.stringify(options.body);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function getRequestBodyFormData({
|
|
166
|
+
body,
|
|
167
|
+
}: Pick<APIRequestInit, 'body'>): FormData | undefined {
|
|
168
|
+
if (body instanceof FormData) return body;
|
|
169
|
+
if (body === null) return;
|
|
170
|
+
|
|
171
|
+
if (body instanceof Blob) throw new Error('Blob not supported in form-data');
|
|
172
|
+
if (body instanceof ArrayBuffer)
|
|
173
|
+
throw new Error('ArrayBuffer not supported in form-data');
|
|
174
|
+
if (body instanceof URLSearchParams)
|
|
175
|
+
throw new Error('URLSearchParams not supported in form-data');
|
|
176
|
+
if (body instanceof ReadableStream)
|
|
177
|
+
throw new Error('ReadableStream not supported in form-data');
|
|
178
|
+
if (typeof body === 'string')
|
|
179
|
+
throw new Error('String not supported in form-data');
|
|
180
|
+
if (typeof body !== 'object')
|
|
181
|
+
throw new Error(`Unsupported body type ${typeof body} in form-data.`);
|
|
182
|
+
|
|
183
|
+
const formData = new FormData();
|
|
184
|
+
|
|
185
|
+
const process = (key: string, value: any) => {
|
|
186
|
+
if (typeof value === 'string' || value instanceof Blob) {
|
|
187
|
+
formData.append(key, value);
|
|
188
|
+
} else {
|
|
189
|
+
formData.append(key, JSON.stringify(value));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
Object.entries(body)
|
|
194
|
+
.filter(([_, value]) => typeof value !== 'undefined' && value !== null)
|
|
195
|
+
.forEach(([key, value]) => {
|
|
196
|
+
if (Array.isArray(value)) {
|
|
197
|
+
value.forEach((v) => process(key, v));
|
|
198
|
+
} else {
|
|
199
|
+
process(key, value);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return formData;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function getBodyContentType(body: APIRequestInit['body']) {
|
|
207
|
+
if (!body) return;
|
|
208
|
+
if (body instanceof Blob) return body.type || 'application/octet-stream';
|
|
209
|
+
if (typeof body === 'string') return 'text/plain';
|
|
210
|
+
if (!(body instanceof FormData)) return 'application/json';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function getResponseBody<T>(response: Response): Promise<T | undefined> {
|
|
214
|
+
if (response.status === 204 || response.headers.get('Content-Length') === '0')
|
|
215
|
+
return undefined;
|
|
216
|
+
|
|
217
|
+
const contentType = response.headers.get('Content-Type')?.toLowerCase();
|
|
218
|
+
const isJSON =
|
|
219
|
+
contentType?.includes('/json') || contentType?.includes('+json');
|
|
220
|
+
|
|
221
|
+
// parse response (falling back to .text() when necessary)
|
|
222
|
+
if (isJSON) return response.json();
|
|
223
|
+
|
|
224
|
+
return response.text() as Promise<T>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// To have definitely typed headers without a conversion to stings
|
|
228
|
+
export type HeadersOptions =
|
|
229
|
+
| HeadersInit
|
|
230
|
+
| Record<string, string | number | boolean | null | undefined>;
|
|
231
|
+
|
|
232
|
+
export type APIRequestInit = {
|
|
233
|
+
readonly method:
|
|
234
|
+
| 'get'
|
|
235
|
+
| 'put'
|
|
236
|
+
| 'post'
|
|
237
|
+
| 'patch'
|
|
238
|
+
| 'delete'
|
|
239
|
+
| 'options'
|
|
240
|
+
| 'head'
|
|
241
|
+
| 'trace';
|
|
242
|
+
readonly url: string;
|
|
243
|
+
readonly parameters?: {
|
|
244
|
+
readonly path?: Record<string, any>;
|
|
245
|
+
readonly cookie?: Record<string, any>;
|
|
246
|
+
readonly header?: Record<string, any>;
|
|
247
|
+
readonly query?: Record<string, any>;
|
|
248
|
+
};
|
|
249
|
+
readonly body?: BodyInit | Record<string, unknown> | null;
|
|
250
|
+
readonly mediaType?: string;
|
|
251
|
+
readonly headers?: HeadersOptions;
|
|
252
|
+
} & Omit<RequestInit, 'headers' | 'method' | 'body'>;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { http, HttpResponse } from 'msw';
|
|
2
|
+
import queryString from 'query-string';
|
|
3
|
+
|
|
4
|
+
import { Services, services } from '../fixtures/api/index.js';
|
|
5
|
+
|
|
6
|
+
export const handlers = [
|
|
7
|
+
http.get<
|
|
8
|
+
ServicePathParameters<
|
|
9
|
+
Services['approvalPolicies']['getApprovalPoliciesId']
|
|
10
|
+
>,
|
|
11
|
+
undefined,
|
|
12
|
+
ServiceResponseParameters<
|
|
13
|
+
Services['approvalPolicies']['getApprovalPoliciesId']
|
|
14
|
+
>
|
|
15
|
+
>(
|
|
16
|
+
openApiToMswPath(
|
|
17
|
+
services.approvalPolicies.getApprovalPoliciesId.schema.url
|
|
18
|
+
),
|
|
19
|
+
({ params: { '0': _, ...path }, request }) => {
|
|
20
|
+
const query = getQueryParameters<
|
|
21
|
+
Services['approvalPolicies']['getApprovalPoliciesId']
|
|
22
|
+
>(request.url);
|
|
23
|
+
|
|
24
|
+
const header = getHeaders<
|
|
25
|
+
Services['approvalPolicies']['getApprovalPoliciesId']
|
|
26
|
+
>(request.headers, 'x-');
|
|
27
|
+
|
|
28
|
+
return HttpResponse.json({
|
|
29
|
+
path,
|
|
30
|
+
query,
|
|
31
|
+
header,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
|
|
36
|
+
http.post<
|
|
37
|
+
ServicePathParameters<Services['entities']['postEntitiesIdDocuments']>,
|
|
38
|
+
ServiceRequestBodyParameters<
|
|
39
|
+
Services['entities']['postEntitiesIdDocuments']
|
|
40
|
+
>,
|
|
41
|
+
ServiceResponseParameters<Services['entities']['postEntitiesIdDocuments']>
|
|
42
|
+
>(
|
|
43
|
+
openApiToMswPath(services.entities.postEntitiesIdDocuments.schema.url),
|
|
44
|
+
async ({ params: { '0': _, ...path }, request }) => {
|
|
45
|
+
const query = getQueryParameters<
|
|
46
|
+
Services['entities']['postEntitiesIdDocuments']
|
|
47
|
+
>(request.url);
|
|
48
|
+
|
|
49
|
+
const header = getHeaders<
|
|
50
|
+
Services['entities']['postEntitiesIdDocuments']
|
|
51
|
+
>(request.headers, 'x-');
|
|
52
|
+
|
|
53
|
+
const body = await request.json();
|
|
54
|
+
|
|
55
|
+
return HttpResponse.json({
|
|
56
|
+
path,
|
|
57
|
+
query,
|
|
58
|
+
header,
|
|
59
|
+
body,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
),
|
|
63
|
+
|
|
64
|
+
http.post<
|
|
65
|
+
ServicePathParameters<Services['files']['postFiles']>,
|
|
66
|
+
ServiceRequestBodyParameters<Services['files']['postFiles']>,
|
|
67
|
+
ServiceResponseParameters<Services['files']['postFiles']>
|
|
68
|
+
>(
|
|
69
|
+
openApiToMswPath(services.files.postFiles.schema.url),
|
|
70
|
+
async ({ request }) => {
|
|
71
|
+
const formData = Object.fromEntries(await request.formData()) as Awaited<
|
|
72
|
+
ReturnType<typeof request.json>
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
const body: Record<string, any> = {};
|
|
76
|
+
|
|
77
|
+
for (const [key, value] of Object.entries(formData)) {
|
|
78
|
+
// some workaround to make it work, since value instanceof (File | Blob) is not working by some reason
|
|
79
|
+
if (
|
|
80
|
+
!value ||
|
|
81
|
+
typeof value === 'string' ||
|
|
82
|
+
value.type !== 'application/octet-stream'
|
|
83
|
+
) {
|
|
84
|
+
body[key] = value;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
body[key] = (value as File).name;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return HttpResponse.json({ body });
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
|
|
94
|
+
http.get<
|
|
95
|
+
ServicePathParameters<Services['files']['getFiles']>,
|
|
96
|
+
undefined,
|
|
97
|
+
ServiceResponseParameters<Services['files']['getFiles']>
|
|
98
|
+
>(openApiToMswPath(services.files.getFiles.schema.url), ({ request }) => {
|
|
99
|
+
const query = getQueryParameters<Services['files']['getFiles']>(
|
|
100
|
+
request.url
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const header = getHeaders<Services['files']['getFiles']>(
|
|
104
|
+
request.headers,
|
|
105
|
+
'x-'
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return HttpResponse.json({
|
|
109
|
+
query,
|
|
110
|
+
header,
|
|
111
|
+
});
|
|
112
|
+
}),
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
function openApiToMswPath(url: string) {
|
|
116
|
+
return `*${url.replace(
|
|
117
|
+
/{(.*?)}/g,
|
|
118
|
+
(substring: string, group: string) => `:${group}`
|
|
119
|
+
)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getQueryParameters<
|
|
123
|
+
T extends
|
|
124
|
+
| {
|
|
125
|
+
getQueryKey: (arg: never) => unknown;
|
|
126
|
+
}
|
|
127
|
+
| {
|
|
128
|
+
getMutationKey: (arg: never) => unknown;
|
|
129
|
+
},
|
|
130
|
+
>(url: string): ServiceQueryParameters<T> {
|
|
131
|
+
return queryString.parse(url.split('?')[1]) as ServiceQueryParameters<T>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getHeaders<
|
|
135
|
+
T extends
|
|
136
|
+
| {
|
|
137
|
+
getQueryKey: (arg: never) => unknown;
|
|
138
|
+
}
|
|
139
|
+
| {
|
|
140
|
+
getMutationKey: (arg: never) => unknown;
|
|
141
|
+
},
|
|
142
|
+
>(headers: Headers, prefix: string): ServiceHeaderParameters<T> {
|
|
143
|
+
return Array.from(headers.entries()).reduce(
|
|
144
|
+
(acc, [headerKey, headerValue]) => {
|
|
145
|
+
if (!headerKey.toLowerCase().startsWith(prefix.toLowerCase())) return acc;
|
|
146
|
+
if (typeof headerValue !== 'string') return acc;
|
|
147
|
+
return {
|
|
148
|
+
...acc,
|
|
149
|
+
[headerKey]: headerValue,
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
{} as Record<string, string>
|
|
153
|
+
) as ServiceHeaderParameters<T>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
type ServiceHeaderParameters<
|
|
157
|
+
T extends
|
|
158
|
+
| {
|
|
159
|
+
getQueryKey: (arg: never) => unknown;
|
|
160
|
+
}
|
|
161
|
+
| {
|
|
162
|
+
getMutationKey: (arg: never) => unknown;
|
|
163
|
+
},
|
|
164
|
+
> = T extends {
|
|
165
|
+
getQueryKey: (arg: infer QueryKey) => unknown;
|
|
166
|
+
}
|
|
167
|
+
? QueryKey extends { header?: infer Parameters }
|
|
168
|
+
? Parameters
|
|
169
|
+
: unknown
|
|
170
|
+
: T extends {
|
|
171
|
+
getMutationKey: (arg: infer MutationKey) => unknown;
|
|
172
|
+
}
|
|
173
|
+
? MutationKey extends { header?: infer Parameters }
|
|
174
|
+
? Parameters
|
|
175
|
+
: unknown
|
|
176
|
+
: never;
|
|
177
|
+
|
|
178
|
+
type ServicePathParameters<
|
|
179
|
+
T extends
|
|
180
|
+
| {
|
|
181
|
+
getQueryKey: (arg: never) => unknown;
|
|
182
|
+
}
|
|
183
|
+
| {
|
|
184
|
+
getMutationKey: (arg: never) => unknown;
|
|
185
|
+
},
|
|
186
|
+
> = T extends {
|
|
187
|
+
getQueryKey: (arg: infer QueryKey) => unknown;
|
|
188
|
+
}
|
|
189
|
+
? QueryKey extends { path?: infer Parameters }
|
|
190
|
+
? Parameters & { 0: string }
|
|
191
|
+
: {}
|
|
192
|
+
: T extends {
|
|
193
|
+
getMutationKey: (arg: infer MutationKey) => unknown;
|
|
194
|
+
}
|
|
195
|
+
? MutationKey extends { path?: infer Parameters }
|
|
196
|
+
? Parameters & { 0: string }
|
|
197
|
+
: {}
|
|
198
|
+
: never;
|
|
199
|
+
|
|
200
|
+
type ServiceQueryParameters<
|
|
201
|
+
T extends
|
|
202
|
+
| {
|
|
203
|
+
getQueryKey: (arg: never) => unknown;
|
|
204
|
+
}
|
|
205
|
+
| {
|
|
206
|
+
getMutationKey: (arg: never) => unknown;
|
|
207
|
+
},
|
|
208
|
+
> = T extends {
|
|
209
|
+
getQueryKey: (arg: infer QueryKey) => unknown;
|
|
210
|
+
}
|
|
211
|
+
? QueryKey extends { query?: infer Parameters }
|
|
212
|
+
? Parameters
|
|
213
|
+
: unknown
|
|
214
|
+
: T extends {
|
|
215
|
+
getMutationKey: (arg: infer MutationKey) => unknown;
|
|
216
|
+
}
|
|
217
|
+
? MutationKey extends { query?: infer Parameters }
|
|
218
|
+
? Parameters
|
|
219
|
+
: unknown
|
|
220
|
+
: never;
|
|
221
|
+
|
|
222
|
+
type ServiceRequestBodyParameters<
|
|
223
|
+
T extends {
|
|
224
|
+
mutationFn: (...args: never[]) => unknown;
|
|
225
|
+
},
|
|
226
|
+
> = T extends {
|
|
227
|
+
mutationFn: (...args: never[]) => unknown;
|
|
228
|
+
}
|
|
229
|
+
? Parameters<T['mutationFn']>[1] extends infer Options
|
|
230
|
+
? Options extends { body: infer Body }
|
|
231
|
+
? Body
|
|
232
|
+
: never
|
|
233
|
+
: never
|
|
234
|
+
: never;
|
|
235
|
+
|
|
236
|
+
type ServiceResponseParameters<
|
|
237
|
+
T extends
|
|
238
|
+
| {
|
|
239
|
+
queryFn: (...args: never[]) => unknown;
|
|
240
|
+
}
|
|
241
|
+
| {
|
|
242
|
+
mutationFn: (...args: never[]) => unknown;
|
|
243
|
+
},
|
|
244
|
+
> = T extends {
|
|
245
|
+
queryFn: (...args: never[]) => unknown;
|
|
246
|
+
}
|
|
247
|
+
? Awaited<ReturnType<T['queryFn']>>
|
|
248
|
+
: T extends {
|
|
249
|
+
mutationFn: (...args: never[]) => unknown;
|
|
250
|
+
}
|
|
251
|
+
? Awaited<ReturnType<T['mutationFn']>>
|
|
252
|
+
: never;
|