@openapi-qraft/react 2.0.0-next.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +154 -1
  2. package/dist/cjs/callbacks/ensureInfiniteQueryData.cjs +10 -0
  3. package/dist/cjs/callbacks/ensureInfiniteQueryData.cjs.map +1 -0
  4. package/dist/cjs/callbacks/ensureQueryData.cjs +10 -0
  5. package/dist/cjs/callbacks/ensureQueryData.cjs.map +1 -0
  6. package/dist/cjs/callbacks/index.cjs +4 -0
  7. package/dist/cjs/callbacks/index.cjs.map +1 -1
  8. package/dist/cjs/callbacks/useQueries.cjs +1 -0
  9. package/dist/cjs/callbacks/useQueries.cjs.map +1 -1
  10. package/dist/cjs/callbacks/useSuspenseQueries.cjs +1 -0
  11. package/dist/cjs/callbacks/useSuspenseQueries.cjs.map +1 -1
  12. package/dist/cjs/lib/callQueryClientFetchMethod.cjs.map +1 -1
  13. package/dist/cjs/lib/requestFn.cjs +67 -64
  14. package/dist/cjs/lib/requestFn.cjs.map +1 -1
  15. package/dist/cjs/lib/responseUtils.cjs +41 -0
  16. package/dist/cjs/lib/responseUtils.cjs.map +1 -0
  17. package/dist/cjs/lib/useComposeUseQueryOptions.cjs +1 -2
  18. package/dist/cjs/lib/useComposeUseQueryOptions.cjs.map +1 -1
  19. package/dist/cjs/qraftAPIClient.cjs +2 -2
  20. package/dist/cjs/qraftAPIClient.cjs.map +1 -1
  21. package/dist/cjs/unstable__responseUtils.cjs +9 -0
  22. package/dist/cjs/unstable__responseUtils.cjs.map +1 -0
  23. package/dist/esm/callbacks/ensureInfiniteQueryData.js +8 -0
  24. package/dist/esm/callbacks/ensureInfiniteQueryData.js.map +1 -0
  25. package/dist/esm/callbacks/ensureQueryData.js +8 -0
  26. package/dist/esm/callbacks/ensureQueryData.js.map +1 -0
  27. package/dist/esm/callbacks/index.js +2 -0
  28. package/dist/esm/callbacks/index.js.map +1 -1
  29. package/dist/esm/callbacks/useQueries.js +1 -0
  30. package/dist/esm/callbacks/useQueries.js.map +1 -1
  31. package/dist/esm/callbacks/useSuspenseQueries.js +1 -0
  32. package/dist/esm/callbacks/useSuspenseQueries.js.map +1 -1
  33. package/dist/esm/lib/callQueryClientFetchMethod.js.map +1 -1
  34. package/dist/esm/lib/requestFn.js +66 -63
  35. package/dist/esm/lib/requestFn.js.map +1 -1
  36. package/dist/esm/lib/responseUtils.js +38 -0
  37. package/dist/esm/lib/responseUtils.js.map +1 -0
  38. package/dist/esm/lib/useComposeUseQueryOptions.js +1 -2
  39. package/dist/esm/lib/useComposeUseQueryOptions.js.map +1 -1
  40. package/dist/esm/qraftAPIClient.js +2 -2
  41. package/dist/esm/qraftAPIClient.js.map +1 -1
  42. package/dist/esm/unstable__responseUtils.js +2 -0
  43. package/dist/esm/unstable__responseUtils.js.map +1 -0
  44. package/dist/types/callbacks/ensureInfiniteQueryData.d.ts +8 -0
  45. package/dist/types/callbacks/ensureInfiniteQueryData.d.ts.map +1 -0
  46. package/dist/types/callbacks/ensureQueryData.d.ts +8 -0
  47. package/dist/types/callbacks/ensureQueryData.d.ts.map +1 -0
  48. package/dist/types/callbacks/index.d.ts +2 -0
  49. package/dist/types/callbacks/index.d.ts.map +1 -1
  50. package/dist/types/callbacks/useQueries.d.ts.map +1 -1
  51. package/dist/types/callbacks/useSuspenseQueries.d.ts.map +1 -1
  52. package/dist/types/index.d.ts +1 -2
  53. package/dist/types/index.d.ts.map +1 -1
  54. package/dist/types/lib/callQueryClientFetchMethod.d.ts +1 -1
  55. package/dist/types/lib/callQueryClientFetchMethod.d.ts.map +1 -1
  56. package/dist/types/lib/requestFn.d.ts +24 -5
  57. package/dist/types/lib/requestFn.d.ts.map +1 -1
  58. package/dist/types/lib/responseUtils.d.ts +13 -0
  59. package/dist/types/lib/responseUtils.d.ts.map +1 -0
  60. package/dist/types/lib/useComposeUseQueryOptions.d.ts.map +1 -1
  61. package/dist/types/qraftAPIClient.d.ts +1 -1
  62. package/dist/types/qraftAPIClient.d.ts.map +1 -1
  63. package/dist/types/unstable__responseUtils.d.ts +2 -0
  64. package/dist/types/unstable__responseUtils.d.ts.map +1 -0
  65. package/package.json +24 -19
  66. package/src/callbacks/ensureInfiniteQueryData.ts +29 -0
  67. package/src/callbacks/ensureQueryData.ts +29 -0
  68. package/src/callbacks/index.ts +2 -0
  69. package/src/callbacks/useQueries.ts +1 -0
  70. package/src/callbacks/useSuspenseQueries.ts +1 -0
  71. package/src/index.ts +6 -8
  72. package/src/lib/callQueryClientFetchMethod.ts +3 -1
  73. package/src/lib/requestFn.ts +96 -120
  74. package/src/lib/responseUtils.ts +74 -0
  75. package/src/lib/useComposeUseQueryOptions.ts +0 -1
  76. package/src/qraftAPIClient.ts +5 -2
  77. package/src/unstable__responseUtils.ts +1 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openapi-qraft/react",
3
- "version": "2.0.0-next.8",
4
- "description": "API client for React, providing type-safe requests and dynamic Tanstack Query React Hooks via a modular, Proxy-based architecture.",
3
+ "version": "2.0.0",
4
+ "description": "OpenAPI client for React, providing type-safe requests and dynamic TanStack Query React Hooks via a modular, Proxy-based architecture.",
5
5
  "scripts": {
6
6
  "build": "NODE_ENV=production rollup --config rollup.config.mjs && tsc --project tsconfig.build.json --emitDeclarationOnly",
7
7
  "dev": "rimraf dist/ && tsc --project tsconfig.build.json --watch --outDir ./dist/esm --noEmitOnError false",
@@ -15,34 +15,34 @@
15
15
  },
16
16
  "sideEffects": false,
17
17
  "dependencies": {
18
- "@openapi-qraft/tanstack-query-react-types": "2.0.0-next.8"
18
+ "@openapi-qraft/tanstack-query-react-types": "2.0.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@openapi-qraft/cli": "2.0.0-next.8",
21
+ "@openapi-qraft/cli": "2.0.0",
22
22
  "@openapi-qraft/eslint-config": "1.0.0",
23
- "@openapi-qraft/openapi-typescript-plugin": "^1.2.0-next.8",
24
- "@openapi-qraft/rollup-config": "1.1.0-next.0",
25
- "@openapi-qraft/tanstack-query-react-plugin": "2.0.0-next.8",
23
+ "@openapi-qraft/openapi-typescript-plugin": "^1.2.0",
24
+ "@openapi-qraft/rollup-config": "1.1.0",
25
+ "@openapi-qraft/tanstack-query-react-plugin": "2.0.0",
26
26
  "@openapi-qraft/test-fixtures": "1.0.0",
27
- "@tanstack/react-query": "^5.17.3",
27
+ "@tanstack/react-query": "^5.56.2",
28
28
  "@testing-library/jest-dom": "^5.17.0",
29
29
  "@testing-library/react": "^13.4.0",
30
30
  "@testing-library/user-event": "^14.5.2",
31
31
  "@types/jscodeshift": "^0.11.11",
32
- "@types/react": "^18.2.47",
33
- "@vitejs/plugin-react": "latest",
34
- "@vitest/coverage-v8": "^1.1.3",
35
- "eslint": "^9.0.0",
36
- "globals": "^15.0.0",
32
+ "@types/react": "^18.3.5",
33
+ "@vitejs/plugin-react": "^4.3.1",
34
+ "@vitest/coverage-v8": "^2.1.8",
35
+ "eslint": "^9.16.0",
36
+ "globals": "^15.9.0",
37
37
  "jscodeshift": "^17.0.0",
38
- "jsdom": "latest",
39
- "msw": "^2.0.12",
38
+ "jsdom": "^25.0.0",
39
+ "msw": "^2.6.8",
40
40
  "prettier": "^3.3.3",
41
- "query-string": "^8.1.0",
42
- "react": "^18.2.0",
41
+ "query-string": "^8.2.0",
42
+ "react": "^18.3.1",
43
43
  "rimraf": "^5.0.10",
44
- "rollup": "~4.18.0",
45
- "vitest": "^1.4.0"
44
+ "rollup": "~4.22.4",
45
+ "vitest": "^2.1.8"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@tanstack/query-core": "^5.0.0",
@@ -77,6 +77,11 @@
77
77
  "require": "./dist/cjs/qraftPredefinedParametersRequestFn.cjs",
78
78
  "types": "./dist/types/qraftPredefinedParametersRequestFn.d.ts"
79
79
  },
80
+ "./unstable__responseUtils": {
81
+ "import": "./dist/esm/unstable__responseUtils.js",
82
+ "require": "./dist/cjs/unstable__responseUtils.cjs",
83
+ "types": "./dist/types/unstable__responseUtils.d.ts"
84
+ },
80
85
  "./package.json": "./package.json"
81
86
  },
82
87
  "typesVersions": {
@@ -0,0 +1,29 @@
1
+ import type { ServiceOperationFetchInfiniteQuery } from '@openapi-qraft/tanstack-query-react-types';
2
+ import type { DefaultError } from '@tanstack/query-core';
3
+ import type { CreateAPIQueryClientOptions } from '../qraftAPIClient.js';
4
+ import { callQueryClientMethodWithQueryKey } from '../lib/callQueryClientFetchMethod.js';
5
+
6
+ export const ensureInfiniteQueryData: <
7
+ TSchema extends { url: string; method: 'get' | 'head' | 'options' },
8
+ TData,
9
+ TParams,
10
+ >(
11
+ qraftOptions: CreateAPIQueryClientOptions,
12
+ schema: TSchema,
13
+ args: Parameters<
14
+ ServiceOperationFetchInfiniteQuery<
15
+ TSchema,
16
+ TData,
17
+ TParams,
18
+ DefaultError
19
+ >['ensureInfiniteQueryData']
20
+ >
21
+ ) => Promise<TData> = (qraftOptions, schema, args) => {
22
+ return callQueryClientMethodWithQueryKey(
23
+ qraftOptions,
24
+ 'ensureInfiniteQueryData',
25
+ schema,
26
+ true,
27
+ args as never
28
+ ) as never;
29
+ };
@@ -0,0 +1,29 @@
1
+ import type { ServiceOperationFetchQuery } from '@openapi-qraft/tanstack-query-react-types';
2
+ import type { DefaultError } from '@tanstack/query-core';
3
+ import type { CreateAPIQueryClientOptions } from '../qraftAPIClient.js';
4
+ import { callQueryClientMethodWithQueryKey } from '../lib/callQueryClientFetchMethod.js';
5
+
6
+ export const ensureQueryData: <
7
+ TSchema extends { url: string; method: 'get' | 'head' | 'options' },
8
+ TData,
9
+ TParams,
10
+ >(
11
+ qraftOptions: CreateAPIQueryClientOptions,
12
+ schema: TSchema,
13
+ args: Parameters<
14
+ ServiceOperationFetchQuery<
15
+ TSchema,
16
+ TData,
17
+ TParams,
18
+ DefaultError
19
+ >['ensureQueryData']
20
+ >
21
+ ) => Promise<TData> = (qraftOptions, schema, args) => {
22
+ return callQueryClientMethodWithQueryKey(
23
+ qraftOptions,
24
+ 'ensureQueryData',
25
+ schema,
26
+ false,
27
+ args as never
28
+ ) as never;
29
+ };
@@ -26,8 +26,10 @@ export { isMutating } from './isMutating.js';
26
26
  export { useIsFetching } from './useIsFetching.js';
27
27
  export { fetchQuery } from './fetchQuery.js';
28
28
  export { prefetchQuery } from './prefetchQuery.js';
29
+ export { ensureQueryData } from './ensureQueryData.js';
29
30
  export { fetchInfiniteQuery } from './fetchInfiniteQuery.js';
30
31
  export { prefetchInfiniteQuery } from './prefetchInfiniteQuery.js';
32
+ export { ensureInfiniteQueryData } from './ensureInfiniteQueryData.js';
31
33
  export { getQueryState } from './getQueryState.js';
32
34
  export { getInfiniteQueryState } from './getInfiniteQueryState.js';
33
35
  export { operationInvokeFn } from './operationInvokeFn.js';
@@ -28,6 +28,7 @@ export const useQueries: (
28
28
  return useQueriesTanstack(
29
29
  {
30
30
  ...options,
31
+ // @ts-expect-error - Too complex to type
31
32
  queries: options.queries.map((queryOptions) => {
32
33
  const optionsWithQueryKey =
33
34
  'parameters' in queryOptions
@@ -28,6 +28,7 @@ export const useSuspenseQueries: (
28
28
  return useSuspenseQueriesTanstack(
29
29
  {
30
30
  ...options,
31
+ // @ts-expect-error - Too complex to type
31
32
  queries: options.queries.map((queryOptions) => {
32
33
  const optionsWithQueryKey =
33
34
  'parameters' in queryOptions
package/src/index.ts CHANGED
@@ -16,13 +16,11 @@ export {
16
16
  urlSerializer,
17
17
  bodySerializer,
18
18
  mergeHeaders,
19
+ type RequestFn,
19
20
  type RequestFnResponse,
20
- } from './lib/requestFn.js';
21
-
22
- export type {
23
- RequestFnPayload,
24
- RequestFnOptions,
25
- HeadersOptions,
26
- OperationSchema,
27
- RequestFn,
21
+ type HeadersOptions,
22
+ type RequestFnOptions,
23
+ type RequestFnInfo,
24
+ type OperationSchema,
25
+ type RequestFnPayload,
28
26
  } from './lib/requestFn.js';
@@ -84,8 +84,10 @@ type QueryClientMethodArgs<QMethod extends keyof QueryClientPrototype> = [
84
84
  type QueryKeyMethods =
85
85
  | QueryKeyMethod<'fetchQuery'>
86
86
  | QueryKeyMethod<'prefetchQuery'>
87
+ | QueryKeyMethod<'ensureQueryData'>
87
88
  | QueryKeyMethod<'fetchInfiniteQuery'>
88
- | QueryKeyMethod<'prefetchInfiniteQuery'>;
89
+ | QueryKeyMethod<'prefetchInfiniteQuery'>
90
+ | QueryKeyMethod<'ensureInfiniteQueryData'>;
89
91
 
90
92
  type QueryClientPrototype = typeof QueryClient.prototype;
91
93
 
@@ -1,12 +1,11 @@
1
1
  import type {
2
2
  HeadersOptions,
3
3
  OperationSchema,
4
- RequestFn,
5
4
  RequestFnInfo,
6
5
  RequestFnOptions,
7
- RequestFnPayload,
8
6
  RequestFnResponse,
9
7
  } from '@openapi-qraft/tanstack-query-react-types';
8
+ import { processResponse, resolveResponse } from './responseUtils.js';
10
9
 
11
10
  /**
12
11
  * This function is used to make a request to a specified endpoint.
@@ -35,7 +34,7 @@ export async function requestFn<TData, TError>(
35
34
 
36
35
  /**
37
36
  * This function is used to make a request to a specified endpoint.
38
- * It's needed to create a custom `requestFn` with a custom `urlSerializer`
37
+ * It's necessary to create a custom `requestFn` with a custom `urlSerializer`
39
38
  * and `bodySerializer`, with the tree-shaking of the default `requestFn`
40
39
  * and its serializers.
41
40
  *
@@ -56,24 +55,18 @@ export async function baseRequestFn<TData, TError>(
56
55
  ): Promise<RequestFnResponse<TData, TError>> {
57
56
  const { parameters, headers, body, ...requestInfoRest } = requestInfo;
58
57
 
59
- const requestBody = options.bodySerializer(requestSchema, requestInfo);
58
+ const requestPayload = options.bodySerializer(requestSchema, body);
60
59
 
61
60
  const baseFetch = options.fetch ?? fetch;
62
61
 
63
62
  return baseFetch(options.urlSerializer(requestSchema, requestInfo), {
64
63
  method: requestSchema.method.toUpperCase(),
65
- body: requestBody,
64
+ body: requestPayload?.body,
66
65
  headers: mergeHeaders(
67
- {
68
- Accept: 'application/json',
69
- 'Content-Type': requestSchema.mediaType ?? getBodyContentType(body),
70
- },
66
+ { Accept: 'application/json' },
67
+ requestPayload?.headers,
71
68
  headers,
72
- parameters?.header,
73
- requestBody instanceof FormData
74
- ? // remove `Content-Type` if the serialized body is FormData; the browser will correctly set Content-Type & boundary expression
75
- { 'Content-Type': null }
76
- : undefined
69
+ parameters?.header
77
70
  ),
78
71
  ...requestInfoRest,
79
72
  })
@@ -98,7 +91,7 @@ export async function baseRequestFn<TData, TError>(
98
91
  */
99
92
  export function urlSerializer(
100
93
  schema: OperationSchema,
101
- info: RequestFnPayload
94
+ info: Pick<RequestFnInfo, 'baseUrl' | 'parameters'>
102
95
  ): string {
103
96
  const path = schema.url.replace(
104
97
  /{(.*?)}/g,
@@ -183,47 +176,101 @@ export function mergeHeaders(...allHeaders: (HeadersOptions | undefined)[]) {
183
176
 
184
177
  export function bodySerializer(
185
178
  schema: OperationSchema,
186
- info: RequestFnPayload
179
+ body: RequestFnInfo['body']
187
180
  ) {
188
- if (info.body === undefined) return;
189
-
190
181
  if (
191
182
  schema.method === 'get' ||
192
183
  schema.method === 'head' ||
193
184
  schema.method === 'options'
194
185
  )
195
- return;
186
+ return undefined;
187
+
188
+ if (body === undefined || body === null) return undefined;
189
+
190
+ if (typeof body === 'string') {
191
+ return {
192
+ body,
193
+ headers: {
194
+ 'Content-Type':
195
+ // prefer text/* media type
196
+ schema.mediaType?.find((mediaType) => mediaType.includes('text/')) ??
197
+ // prefer JSON media type, assume that body is serialized as JSON
198
+ schema.mediaType?.find((mediaType) => mediaType.includes('/json')) ??
199
+ 'text/plain',
200
+ },
201
+ };
202
+ }
196
203
 
197
- if (schema.mediaType?.includes('/form-data'))
198
- return getRequestBodyFormData(info);
204
+ if (body instanceof FormData) {
205
+ return {
206
+ body,
207
+ headers: {
208
+ // remove `Content-Type` if the serialized body is FormData;
209
+ // the browser will correctly set Content-Type & boundary expression
210
+ 'Content-Type': null,
211
+ },
212
+ };
213
+ }
199
214
 
200
- if (
201
- !schema.mediaType?.includes('/json') &&
202
- (typeof info.body === 'string' ||
203
- info.body instanceof Blob ||
204
- info.body instanceof FormData)
205
- ) {
206
- return info.body;
215
+ if (body instanceof Blob) {
216
+ return {
217
+ body,
218
+ headers: {
219
+ 'Content-Type':
220
+ body.type ||
221
+ schema.mediaType?.find(
222
+ (mediaType) =>
223
+ !(
224
+ mediaType.includes('text/') &&
225
+ mediaType.includes('/form-data') &&
226
+ mediaType.includes('/json')
227
+ )
228
+ ) ||
229
+ 'application/octet-stream',
230
+ },
231
+ };
232
+ }
233
+
234
+ let jsonMediaType: string | null = null;
235
+ let formDataMediaType: string | null = null;
236
+
237
+ if (schema.mediaType) {
238
+ for (let i = 0; i < schema.mediaType.length; i++) {
239
+ const mediaType = schema.mediaType[i];
240
+ if (mediaType.includes('/json')) jsonMediaType = mediaType;
241
+ else if (mediaType.includes('/form-data')) formDataMediaType = mediaType;
242
+ }
243
+ }
244
+
245
+ if (formDataMediaType) {
246
+ if (
247
+ !jsonMediaType ||
248
+ // Prefer FormData serialization if one of the fields is a Blob
249
+ Object.values(body).some((value) => value instanceof Blob)
250
+ ) {
251
+ return {
252
+ body: getRequestBodyFormData(body),
253
+ headers: {
254
+ // remove `Content-Type` if the serialized body is FormData;
255
+ // the browser will correctly set Content-Type & boundary expression
256
+ 'Content-Type': null,
257
+ },
258
+ };
259
+ }
207
260
  }
208
261
 
209
- return JSON.stringify(info.body);
262
+ return {
263
+ body: JSON.stringify(body),
264
+ headers: {
265
+ 'Content-Type': jsonMediaType ?? 'application/json',
266
+ },
267
+ };
210
268
  }
211
269
 
212
- function getRequestBodyFormData({
213
- body,
214
- }: Pick<RequestFnPayload, 'body'>): FormData | undefined {
270
+ function getRequestBodyFormData(
271
+ body: NonNullable<RequestFnInfo['body']>
272
+ ): FormData {
215
273
  if (body instanceof FormData) return body;
216
- if (body === null) return;
217
-
218
- if (body instanceof Blob) throw new Error('Blob not supported in form-data');
219
- if (body instanceof ArrayBuffer)
220
- throw new Error('ArrayBuffer not supported in form-data');
221
- if (body instanceof URLSearchParams)
222
- throw new Error('URLSearchParams not supported in form-data');
223
- if (body instanceof ReadableStream)
224
- throw new Error('ReadableStream not supported in form-data');
225
- if (typeof body === 'string')
226
- throw new Error('String not supported in form-data');
227
274
  if (typeof body !== 'object')
228
275
  throw new Error(`Unsupported body type ${typeof body} in form-data.`);
229
276
 
@@ -250,81 +297,6 @@ function getRequestBodyFormData({
250
297
  return formData;
251
298
  }
252
299
 
253
- function getBodyContentType(body: RequestFnPayload['body']) {
254
- if (!body) return;
255
- if (body instanceof Blob) return body.type || 'application/octet-stream';
256
- if (typeof body === 'string') return 'text/plain';
257
- if (!(body instanceof FormData)) return 'application/json';
258
- }
259
-
260
- async function processResponse<TData, TError>(
261
- response: Response
262
- ): Promise<RequestFnResponse<TData, TError>> {
263
- if (response.status === 204 || response.headers.get('Content-Length') === '0')
264
- return (
265
- response.ok ? { data: {}, response } : { error: {}, response }
266
- ) as RequestFnResponse<TData, TError>;
267
-
268
- if (isJsonResponse(response)) {
269
- // clone response before parsing every time to allow multiple reads
270
- const jsonResponse = response.clone().json();
271
- return resolveResponse(
272
- response,
273
- response.ok ? jsonResponse : Promise.reject(await jsonResponse)
274
- );
275
- }
276
-
277
- const jsonResponse = new Promise<TData>((resolve, reject) =>
278
- // attempt to parse JSON for successful responses, otherwise fail
279
- response.clone().text().then(JSON.parse).then(resolve, reject)
280
- );
281
-
282
- return resolveResponse(
283
- response,
284
- response.ok ? jsonResponse : Promise.reject(await jsonResponse)
285
- );
286
- }
287
-
288
- function isJsonResponse(response: Response) {
289
- const contentType = response.headers.get('Content-Type')?.toLowerCase();
290
- return contentType?.includes('/json') || contentType?.includes('+json');
291
- }
292
-
293
- function resolveResponse<TData, TError>(
294
- error: Error
295
- ): Promise<RequestFnResponse<TData, TError>>;
296
- function resolveResponse<TData, TError>(
297
- responseToReturn: Response,
298
- responsePromise: Promise<TData>
299
- ): Promise<RequestFnResponse<TData, TError>>;
300
- function resolveResponse<TData, TError>(
301
- responseToReturn: Response | Error,
302
- responsePromise?: Promise<TData>
303
- ): Promise<RequestFnResponse<TData, TError>> {
304
- if (!responsePromise)
305
- return Promise.reject({
306
- error: responseToReturn,
307
- response: undefined,
308
- data: undefined,
309
- });
310
-
311
- return responsePromise
312
- .then(
313
- (data) =>
314
- ({ data, response: responseToReturn }) as RequestFnResponse<
315
- TData,
316
- TError
317
- >
318
- )
319
- .catch(
320
- (error) =>
321
- ({ error, response: responseToReturn }) as RequestFnResponse<
322
- TData,
323
- TError
324
- >
325
- );
326
- }
327
-
328
300
  type WithRequired<T, K extends keyof T> = T & {
329
301
  [_ in K]: {};
330
302
  };
@@ -333,8 +305,12 @@ export type {
333
305
  RequestFn,
334
306
  RequestFnResponse,
335
307
  HeadersOptions,
336
- RequestFnPayload,
337
308
  RequestFnOptions,
338
309
  RequestFnInfo,
339
310
  OperationSchema,
340
- };
311
+ } from '@openapi-qraft/tanstack-query-react-types';
312
+
313
+ /**
314
+ * @deprecated use `RequestFnInfo` instead
315
+ */
316
+ export type RequestFnPayload = RequestFnInfo;
@@ -0,0 +1,74 @@
1
+ import type { RequestFnResponse } from '@openapi-qraft/tanstack-query-react-types';
2
+
3
+ /**
4
+ * Processes the response from the server.
5
+ * @beta
6
+ */
7
+ export async function processResponse<TData, TError>(
8
+ response: Response
9
+ ): Promise<RequestFnResponse<TData, TError>> {
10
+ if (response.status === 204 || response.headers.get('Content-Length') === '0')
11
+ return (
12
+ response.ok ? { data: {}, response } : { error: {}, response }
13
+ ) as RequestFnResponse<TData, TError>;
14
+
15
+ const contentType = response.headers.get('Content-Type')?.toLowerCase();
16
+
17
+ const isJsonResponse =
18
+ contentType?.includes('/json') || contentType?.includes('+json');
19
+
20
+ if (isJsonResponse) {
21
+ // clone response before parsing every time to allow multiple reads
22
+ const jsonResponse = response.clone().json();
23
+ return resolveResponse(
24
+ response,
25
+ response.ok ? jsonResponse : Promise.reject(await jsonResponse)
26
+ );
27
+ }
28
+
29
+ const textResponse = response.clone().text() as Promise<TData>;
30
+
31
+ return resolveResponse(
32
+ response,
33
+ response.ok ? textResponse : Promise.reject(await textResponse)
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Resolves the response from the server.
39
+ * @beta
40
+ */
41
+ export function resolveResponse<TData, TError>(
42
+ error: Error
43
+ ): Promise<RequestFnResponse<TData, TError>>;
44
+ export function resolveResponse<TData, TError>(
45
+ responseToReturn: Response,
46
+ responsePromise: Promise<TData>
47
+ ): Promise<RequestFnResponse<TData, TError>>;
48
+ export function resolveResponse<TData, TError>(
49
+ responseToReturn: Response | Error,
50
+ responsePromise?: Promise<TData>
51
+ ): Promise<RequestFnResponse<TData, TError>> {
52
+ if (!responsePromise)
53
+ return Promise.reject({
54
+ error: responseToReturn,
55
+ response: undefined,
56
+ data: undefined,
57
+ });
58
+
59
+ return responsePromise
60
+ .then(
61
+ (data) =>
62
+ ({ data, response: responseToReturn }) as RequestFnResponse<
63
+ TData,
64
+ TError
65
+ >
66
+ )
67
+ .catch(
68
+ (error) =>
69
+ ({ error, response: responseToReturn }) as RequestFnResponse<
70
+ TData,
71
+ TError
72
+ >
73
+ );
74
+ }
@@ -24,7 +24,6 @@ export function useComposeUseQueryOptions(
24
24
 
25
25
  const queryFn =
26
26
  options?.queryFn ??
27
- // @ts-expect-error - Too complex to type
28
27
  function ({ queryKey: [, queryParams], signal, meta, pageParam }) {
29
28
  return qraftOptions
30
29
  .requestFn(schema, {
@@ -116,11 +116,12 @@ export function qraftAPIClient<
116
116
  if (
117
117
  callbackName !== 'operationInvokeFn' &&
118
118
  callbackName !== 'getQueryKey' &&
119
- callbackName !== 'getMutationKey'
119
+ callbackName !== 'getMutationKey' &&
120
+ callbackName !== 'getInfiniteQueryKey'
120
121
  )
121
122
  if (!options || !('queryClient' in options && options.queryClient))
122
123
  throw new Error(
123
- `'qraft.<service>.<operation>.${String(callbackName)}()' requires 'queryClient' in options.`
124
+ `'qraft.<service>.<operation>.${String(callbackName)}()' requires 'queryClient' in 'createAPIClient(...)' options.`
124
125
  );
125
126
 
126
127
  // @ts-expect-error - Too complex union type
@@ -223,6 +224,8 @@ type QueryOperationCallbacks = Extract<
223
224
  | 'prefetchInfiniteQuery'
224
225
  | 'prefetchQuery'
225
226
  | 'refetchQueries'
227
+ | 'ensureQueryData'
228
+ | 'ensureInfiniteQueryData'
226
229
  | 'removeQueries'
227
230
  | 'resetQueries'
228
231
  | 'setInfiniteQueryData'
@@ -0,0 +1 @@
1
+ export { resolveResponse, processResponse } from './lib/responseUtils.js';