@openapi-qraft/react 1.0.0-beta.4 → 1.0.0-beta.6

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 (48) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +35 -0
  3. package/dist/ServiceOperation.d.ts +58 -9
  4. package/dist/ServiceOperation.d.ts.map +1 -1
  5. package/dist/callbacks/getMutationKey.d.ts +2 -2
  6. package/dist/callbacks/getMutationKey.d.ts.map +1 -1
  7. package/dist/callbacks/getMutationKey.js +2 -19
  8. package/dist/callbacks/getMutationKey.js.map +1 -1
  9. package/dist/callbacks/useInfiniteQuery.d.ts +0 -6
  10. package/dist/callbacks/useInfiniteQuery.d.ts.map +1 -1
  11. package/dist/callbacks/useInfiniteQuery.js +1 -27
  12. package/dist/callbacks/useInfiniteQuery.js.map +1 -1
  13. package/dist/callbacks/useMutation.d.ts.map +1 -1
  14. package/dist/callbacks/useMutation.js +4 -16
  15. package/dist/callbacks/useMutation.js.map +1 -1
  16. package/dist/callbacks/useMutationState.d.ts +7 -0
  17. package/dist/callbacks/useMutationState.d.ts.map +1 -0
  18. package/dist/callbacks/useMutationState.js +30 -0
  19. package/dist/callbacks/useMutationState.js.map +1 -0
  20. package/dist/callbacks/useSuspenseInfiniteQuery.d.ts +9 -0
  21. package/dist/callbacks/useSuspenseInfiniteQuery.d.ts.map +1 -0
  22. package/dist/callbacks/useSuspenseInfiniteQuery.js +25 -0
  23. package/dist/callbacks/useSuspenseInfiniteQuery.js.map +1 -0
  24. package/dist/callbacks/useSuspenseQuery.d.ts +9 -0
  25. package/dist/callbacks/useSuspenseQuery.d.ts.map +1 -0
  26. package/dist/callbacks/useSuspenseQuery.js +28 -0
  27. package/dist/callbacks/useSuspenseQuery.js.map +1 -0
  28. package/dist/lib/composeMutationKey.d.ts +10 -0
  29. package/dist/lib/composeMutationKey.d.ts.map +1 -0
  30. package/dist/lib/composeMutationKey.js +21 -0
  31. package/dist/lib/composeMutationKey.js.map +1 -0
  32. package/dist/lib/shelfMerge.d.ts +7 -0
  33. package/dist/lib/shelfMerge.d.ts.map +1 -0
  34. package/dist/lib/shelfMerge.js +28 -0
  35. package/dist/lib/shelfMerge.js.map +1 -0
  36. package/package.json +2 -2
  37. package/src/ServiceOperation.ts +156 -8
  38. package/src/callbacks/getMutationKey.ts +6 -34
  39. package/src/callbacks/useInfiniteQuery.ts +1 -36
  40. package/src/callbacks/useMutation.ts +7 -19
  41. package/src/callbacks/useMutationState.ts +73 -0
  42. package/src/callbacks/useSuspenseInfiniteQuery.ts +60 -0
  43. package/src/callbacks/useSuspenseQuery.ts +64 -0
  44. package/src/lib/composeMutationKey.test.ts +43 -0
  45. package/src/lib/composeMutationKey.ts +50 -0
  46. package/src/{callbacks → lib}/shelfMerge.test.ts +1 -1
  47. package/src/lib/shelfMerge.ts +35 -0
  48. package/src/qraftAPIClient.test.tsx +358 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shelfMerge.js","sourceRoot":"","sources":["../../src/lib/shelfMerge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAI,KAAK,GAAG,CAAC,EAAE,GAAG,IAAS;IACnD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,GAAG,CAAC,GAAY,CAAC,GAAG,KAAc,CAAC;gBACnC,OAAO;YACT,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,GAAG,CAAC,GAAY,CAAC,GAAG,KAAc,CAAC;gBACnC,OAAO;YACT,CAAC;YAED,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,GAAG,CAAC,GAAY,CAAC,GAAG,UAAU,CAC5B,KAAK,GAAG,CAAC,EACT,GAAG,CAAC,GAAY,CAAC,EACjB,KAAK,CACG,CAAC;gBACX,OAAO;YACT,CAAC;YAED,GAAG,CAAC,GAAY,CAAC,GAAG,KAAc,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAO,CAAC,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-qraft/react",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "API 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": "rimraf dist/ && tsc --project tsconfig.build.json",
@@ -13,7 +13,7 @@
13
13
  "type": "module",
14
14
  "sideEffects": false,
15
15
  "devDependencies": {
16
- "@openapi-qraft/cli": "~1.0.0-beta.2",
16
+ "@openapi-qraft/cli": "~1.0.0-beta.4",
17
17
  "@openapi-qraft/openapi-typescript": "^1.0.0-beta.1",
18
18
  "@tanstack/react-query": "^5.17.3",
19
19
  "@testing-library/jest-dom": "^5.17.0",
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  DefaultError,
3
3
  InfiniteData,
4
- QueryClient,
5
4
  InfiniteQueryPageParamsOptions,
5
+ Mutation,
6
+ MutationState,
7
+ MutationStatus,
6
8
  NoInfer,
9
+ QueryClient,
7
10
  SetDataOptions,
8
11
  Updater,
9
12
  } from '@tanstack/query-core';
10
- import type {
13
+ import {
11
14
  DefinedInitialDataInfiniteOptions,
12
15
  DefinedInitialDataOptions,
13
16
  DefinedUseInfiniteQueryResult,
@@ -18,6 +21,10 @@ import type {
18
21
  UseMutationOptions,
19
22
  UseMutationResult,
20
23
  UseQueryResult,
24
+ UseSuspenseInfiniteQueryOptions,
25
+ UseSuspenseInfiniteQueryResult,
26
+ UseSuspenseQueryOptions,
27
+ UseSuspenseQueryResult,
21
28
  } from '@tanstack/react-query';
22
29
 
23
30
  export type ServiceOperationQueryKey<S extends { url: string }, T> = [
@@ -32,8 +39,10 @@ export type ServiceOperationInfiniteQueryKey<S extends { url: string }, T> = [
32
39
 
33
40
  export type ServiceOperationMutationKey<
34
41
  S extends Record<'url' | 'method', string>,
35
- T,
36
- > = [Pick<S, 'url' | 'method'>, T];
42
+ T extends unknown,
43
+ > = NonNullable<T> extends never
44
+ ? [Pick<S, 'url' | 'method'>]
45
+ : [Pick<S, 'url' | 'method'>, T];
37
46
 
38
47
  export interface ServiceOperationQuery<
39
48
  TSchema extends { url: string; method: string },
@@ -42,6 +51,8 @@ export interface ServiceOperationQuery<
42
51
  TError = DefaultError,
43
52
  > extends ServiceOperationUseQuery<TSchema, TData, TParams, TError>,
44
53
  ServiceOperationUseInfiniteQuery<TSchema, TData, TParams, TError>,
54
+ ServiceOperationUseSuspenseQueryQuery<TSchema, TData, TParams, TError>,
55
+ ServiceOperationUseSuspenseInfiniteQuery<TSchema, TData, TParams, TError>,
45
56
  ServiceOperationQueryFn<TSchema, TData, TParams>,
46
57
  ServiceOperationGetQueryData<TData, TParams>,
47
58
  ServiceOperationGetInfiniteQueryData<TData, TParams>,
@@ -144,6 +155,56 @@ interface ServiceOperationUseInfiniteQuery<
144
155
  ): DefinedUseInfiniteQueryResult<InfiniteData<TData>, TError | Error>;
145
156
  }
146
157
 
158
+ interface ServiceOperationUseSuspenseInfiniteQuery<
159
+ TSchema extends { url: string; method: string },
160
+ TData,
161
+ TParams = {},
162
+ TError = DefaultError,
163
+ > {
164
+ useSuspenseInfiniteQuery<TPageParam extends TParams>(
165
+ params: TParams,
166
+ options: Omit<
167
+ UseSuspenseInfiniteQueryOptions<
168
+ TData,
169
+ TError,
170
+ InfiniteData<TData>,
171
+ TData,
172
+ | ServiceOperationInfiniteQueryKey<TSchema, TParams>
173
+ | readonly [...ServiceOperationInfiniteQueryKey<TSchema, TParams>],
174
+ PartialParams<TPageParam>
175
+ >,
176
+ | 'queryKey'
177
+ | 'getPreviousPageParam'
178
+ | 'getNextPageParam'
179
+ | 'initialPageParam'
180
+ > &
181
+ InfiniteQueryPageParamsOptions<TData, PartialParams<TPageParam>>,
182
+ queryClient?: QueryClient
183
+ ): UseSuspenseInfiniteQueryResult<InfiniteData<TData>, TError | Error>;
184
+ }
185
+
186
+ interface ServiceOperationUseSuspenseQueryQuery<
187
+ TSchema extends { url: string; method: string },
188
+ TData,
189
+ TParams = {},
190
+ TError = DefaultError,
191
+ > {
192
+ useSuspenseQuery(
193
+ params: TParams,
194
+ options?: Omit<
195
+ UseSuspenseQueryOptions<
196
+ TData,
197
+ TError,
198
+ TData,
199
+ | ServiceOperationQueryKey<TSchema, TParams>
200
+ | readonly [...ServiceOperationQueryKey<TSchema, TParams>]
201
+ >,
202
+ 'queryKey'
203
+ >,
204
+ queryClient?: QueryClient
205
+ ): UseSuspenseQueryResult<TData, TError | Error>;
206
+ }
207
+
147
208
  export interface ServiceOperationMutation<
148
209
  TSchema extends { url: string; method: string },
149
210
  TBody,
@@ -151,6 +212,7 @@ export interface ServiceOperationMutation<
151
212
  TParams,
152
213
  TError = DefaultError,
153
214
  > extends ServiceOperationUseMutation<TSchema, TBody, TData, TParams, TError>,
215
+ ServiceOperationUseMutationState<TSchema, TBody, TData, TParams, TError>,
154
216
  ServiceOperationMutationFn<TSchema, TBody, TData, TParams>,
155
217
  ServiceOperationSetQueryData<TData, TParams> {
156
218
  schema: TSchema;
@@ -164,13 +226,11 @@ interface ServiceOperationUseMutation<
164
226
  TError = DefaultError,
165
227
  > {
166
228
  getMutationKey<T extends TParams>(
167
- params: T
229
+ params: T | undefined
168
230
  ): ServiceOperationMutationKey<TSchema, T>;
169
231
 
170
232
  useMutation<
171
- TVariables extends { body: TBody } & (NonNullable<TParams> extends never
172
- ? {}
173
- : TParams),
233
+ TVariables extends MutationVariables<TBody, TParams>,
174
234
  TContext = unknown,
175
235
  >(
176
236
  params?: undefined,
@@ -196,6 +256,94 @@ interface ServiceOperationUseMutation<
196
256
  ): UseMutationResult<TData, TError | Error, TVariables, TContext>;
197
257
  }
198
258
 
259
+ type MutationFilters<
260
+ TSchema extends { url: string; method: string },
261
+ TBody,
262
+ TData,
263
+ TParams,
264
+ TError = DefaultError,
265
+ TContext = unknown,
266
+ > = {
267
+ /**
268
+ * Match mutation key exactly
269
+ */
270
+ exact?: boolean;
271
+ /**
272
+ * Include mutations matching this predicate function
273
+ */
274
+ predicate?: (
275
+ mutation: Mutation<
276
+ TData,
277
+ TError,
278
+ MutationVariables<TBody, TParams>,
279
+ TContext
280
+ >
281
+ ) => boolean;
282
+
283
+ /**
284
+ * Filter by mutation status
285
+ */
286
+ status?: MutationStatus;
287
+ } & (
288
+ | {
289
+ /**
290
+ * Include mutations matching this mutation key
291
+ */
292
+ mutationKey?: ServiceOperationMutationKey<
293
+ TSchema,
294
+ PartialParams<TParams>
295
+ >;
296
+ }
297
+ | {
298
+ /**
299
+ * Include mutations matching these parameters
300
+ */
301
+ parameters?: PartialParams<TParams>;
302
+ }
303
+ );
304
+
305
+ type MutationVariables<TBody, TParams> = {
306
+ body: TBody;
307
+ } & (NonNullable<TParams> extends never ? {} : TParams);
308
+
309
+ interface ServiceOperationUseMutationState<
310
+ TSchema extends { url: string; method: string },
311
+ TBody,
312
+ TData,
313
+ TParams,
314
+ TError = DefaultError,
315
+ > {
316
+ useMutationState<
317
+ TContext = unknown,
318
+ TResult = MutationState<
319
+ TData,
320
+ TError,
321
+ MutationVariables<TBody, TParams>,
322
+ TContext
323
+ >,
324
+ >(
325
+ options?: {
326
+ filters?: MutationFilters<
327
+ TSchema,
328
+ TBody,
329
+ TData,
330
+ TParams,
331
+ TError,
332
+ TContext
333
+ >;
334
+ select?: (
335
+ mutation: Mutation<
336
+ TData,
337
+ TError,
338
+ MutationVariables<TBody, TParams>,
339
+ TContext
340
+ >
341
+ ) => TResult;
342
+ },
343
+ queryClient?: QueryClient
344
+ ): Array<TResult>;
345
+ }
346
+
199
347
  interface ServiceOperationQueryFn<
200
348
  TSchema extends { url: string; method: string },
201
349
  TData,
@@ -1,47 +1,19 @@
1
+ import { composeMutationKey } from '../lib/composeMutationKey.js';
1
2
  import type { QraftClientOptions } from '../qraftAPIClient.js';
2
3
  import type { RequestClientSchema } from '../RequestClient.js';
3
- import {
4
- ServiceOperationMutation,
5
- ServiceOperationMutationKey,
6
- } from '../ServiceOperation.js';
4
+ import { ServiceOperationMutation } from '../ServiceOperation.js';
7
5
 
8
6
  export const getMutationKey = (
9
7
  qraftOptions: QraftClientOptions | undefined,
10
8
  schema: RequestClientSchema,
11
- args: unknown
12
- ) => {
13
- const [parameters] = args as Parameters<
9
+ args: Parameters<
14
10
  ServiceOperationMutation<
15
11
  RequestClientSchema,
16
12
  unknown,
17
13
  unknown,
18
14
  unknown
19
15
  >['getMutationKey']
20
- >;
21
-
22
- const key: ServiceOperationMutationKey<RequestClientSchema, unknown> = [
23
- {
24
- url: schema.url,
25
- method: schema.method,
26
- },
27
- omitMutationPayload(parameters),
28
- ];
29
-
30
- return key;
16
+ >
17
+ ) => {
18
+ return composeMutationKey(schema, args[0]);
31
19
  };
32
-
33
- function omitMutationPayload<T>(params: T) {
34
- if (!params || typeof params !== 'object')
35
- throw new Error('`params` must be object');
36
-
37
- if ('body' in params || 'requestBody' in params) {
38
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
- const { body, requestBody, ...paramsRest } = params as Record<
40
- 'body' | 'requestBody',
41
- unknown
42
- >;
43
- return paramsRest;
44
- }
45
-
46
- return params as Omit<T, 'body' | 'requestBody'>;
47
- }
@@ -8,6 +8,7 @@ import {
8
8
  UseInfiniteQueryResult,
9
9
  } from '@tanstack/react-query';
10
10
 
11
+ import { shelfMerge } from '../lib/shelfMerge.js';
11
12
  import type { QraftClientOptions } from '../qraftAPIClient.js';
12
13
  import { QraftContext } from '../QraftContext.js';
13
14
  import type { RequestClientSchema } from '../RequestClient.js';
@@ -52,39 +53,3 @@ export const useInfiniteQuery: <
52
53
  ...restArgs
53
54
  ) as never;
54
55
  };
55
-
56
- /**
57
- * Merges objects with provided depth
58
- * @param depth
59
- * @param args
60
- */
61
- export function shelfMerge<T>(depth = 2, ...args: T[]): T {
62
- return args.reduce((acc, arg) => {
63
- if (!arg || typeof arg !== 'object') return acc;
64
-
65
- Object.entries(arg).forEach(([key, value]) => {
66
- if (typeof value !== 'object') {
67
- acc[key as never] = value as never;
68
- return;
69
- }
70
-
71
- if (Array.isArray(value)) {
72
- acc[key as never] = value as never;
73
- return;
74
- }
75
-
76
- if (depth > 1) {
77
- acc[key as never] = shelfMerge(
78
- depth - 1,
79
- acc[key as never],
80
- value
81
- ) as never;
82
- return;
83
- }
84
-
85
- acc[key as never] = value as never;
86
- });
87
-
88
- return acc;
89
- }, {} as T);
90
- }
@@ -6,6 +6,7 @@ import type { DefaultError } from '@tanstack/query-core';
6
6
  import type { UseMutationResult } from '@tanstack/react-query';
7
7
  import { useMutation as useMutationBase } from '@tanstack/react-query';
8
8
 
9
+ import { composeMutationKey } from '../lib/composeMutationKey.js';
9
10
  import type { QraftClientOptions } from '../qraftAPIClient.js';
10
11
  import { QraftContext } from '../QraftContext.js';
11
12
  import type { RequestClientSchema } from '../RequestClient.js';
@@ -53,25 +54,12 @@ export const useMutation: <
53
54
  if (!requestClient) throw new Error(`QraftContext.requestClient not found`);
54
55
 
55
56
  const mutationKey =
56
- parameters && typeof parameters === 'object'
57
- ? ([
58
- {
59
- url: schema.url,
60
- method: schema.method,
61
- },
62
- parameters,
63
- ] as const)
64
- : options && 'mutationKey' in options
65
- ? (options.mutationKey as ServiceOperationMutationKey<
66
- typeof schema,
67
- unknown
68
- >)
69
- : ([
70
- {
71
- url: schema.url,
72
- method: schema.method,
73
- },
74
- ] as const);
57
+ options && 'mutationKey' in options
58
+ ? (options.mutationKey as ServiceOperationMutationKey<
59
+ typeof schema,
60
+ unknown
61
+ >)
62
+ : composeMutationKey(schema, parameters);
75
63
 
76
64
  return useMutationBase(
77
65
  {
@@ -0,0 +1,73 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+
5
+ import type { DefaultError } from '@tanstack/query-core';
6
+ import type { UseMutationResult } from '@tanstack/react-query';
7
+ import { useMutationState as useMutationStateTanstack } from '@tanstack/react-query';
8
+
9
+ import { composeMutationKey } from '../lib/composeMutationKey.js';
10
+ import type { QraftClientOptions } from '../qraftAPIClient.js';
11
+ import { QraftContext } from '../QraftContext.js';
12
+ import type { RequestClientSchema } from '../RequestClient.js';
13
+ import { ServiceOperationMutation } from '../ServiceOperation.js';
14
+
15
+ export const useMutationState: <
16
+ TData = unknown,
17
+ TError = DefaultError,
18
+ TVariables = unknown,
19
+ TContext = unknown,
20
+ >(
21
+ qraftOptions: QraftClientOptions | undefined,
22
+ schema: RequestClientSchema,
23
+ args: Parameters<
24
+ ServiceOperationMutation<
25
+ RequestClientSchema,
26
+ object | undefined,
27
+ TVariables,
28
+ TData
29
+ >['useMutationState']
30
+ >
31
+ ) => UseMutationResult<TData, TError, TVariables, TContext> = (
32
+ qraftOptions,
33
+ schema,
34
+ args
35
+ ) => {
36
+ const [options, ...restArgs] = args;
37
+
38
+ if (
39
+ options?.filters &&
40
+ 'parameters' in options.filters &&
41
+ 'mutationKey' in options.filters
42
+ ) {
43
+ throw new Error(
44
+ `'useMutationState': 'parameters' and 'mutationKey' cannot be used together`
45
+ );
46
+ }
47
+
48
+ const requestClient = useContext(qraftOptions?.context ?? QraftContext)
49
+ ?.requestClient;
50
+
51
+ if (!requestClient) throw new Error(`QraftContext.requestClient not found`);
52
+
53
+ const filters = options?.filters;
54
+
55
+ return useMutationStateTanstack(
56
+ {
57
+ ...options,
58
+ filters:
59
+ filters && 'mutationKey' in filters
60
+ ? filters
61
+ : {
62
+ ...filters,
63
+ mutationKey: composeMutationKey(
64
+ schema,
65
+ filters && 'parameters' in filters
66
+ ? filters.parameters
67
+ : undefined
68
+ ),
69
+ },
70
+ } as never,
71
+ ...restArgs
72
+ ) as never;
73
+ };
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+
5
+ import type { DefaultError, InfiniteData } from '@tanstack/query-core';
6
+ import {
7
+ useSuspenseInfiniteQuery as useSuspenseInfiniteQueryTanstack,
8
+ UseSuspenseInfiniteQueryResult,
9
+ } from '@tanstack/react-query';
10
+
11
+ import { shelfMerge } from '../lib/shelfMerge.js';
12
+ import type { QraftClientOptions } from '../qraftAPIClient.js';
13
+ import { QraftContext } from '../QraftContext.js';
14
+ import type { RequestClientSchema } from '../RequestClient.js';
15
+ import {
16
+ ServiceOperationQuery,
17
+ ServiceOperationQueryKey,
18
+ } from '../ServiceOperation.js';
19
+
20
+ export const useSuspenseInfiniteQuery: <
21
+ TQueryFnData,
22
+ TError = DefaultError,
23
+ TData = InfiniteData<TQueryFnData>,
24
+ >(
25
+ qraftOptions: QraftClientOptions | undefined,
26
+ schema: RequestClientSchema,
27
+ args: Parameters<
28
+ ServiceOperationQuery<
29
+ RequestClientSchema,
30
+ unknown,
31
+ unknown
32
+ >['useSuspenseInfiniteQuery']
33
+ >
34
+ ) => UseSuspenseInfiniteQueryResult<TData, TError> & {
35
+ queryKey: ServiceOperationQueryKey<RequestClientSchema, unknown>;
36
+ } = (qraftOptions, schema, args) => {
37
+ const [params, options, ...restArgs] = args;
38
+
39
+ const requestClient = useContext(qraftOptions?.context ?? QraftContext)
40
+ ?.requestClient;
41
+
42
+ if (!requestClient) throw new Error(`QraftContext.requestClient not found`);
43
+
44
+ return useSuspenseInfiniteQueryTanstack(
45
+ {
46
+ ...options,
47
+ queryKey: [{ url: schema.url, infinite: true }, params as never] as const,
48
+ queryFn:
49
+ options?.queryFn ??
50
+ function ({ queryKey: [, queryParams], signal, meta, pageParam }) {
51
+ return requestClient(schema, {
52
+ parameters: shelfMerge(2, queryParams, pageParam) as never,
53
+ signal,
54
+ meta,
55
+ });
56
+ },
57
+ },
58
+ ...restArgs
59
+ ) as never;
60
+ };
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import { useContext } from 'react';
4
+
5
+ import type { DefaultError } from '@tanstack/query-core';
6
+ import {
7
+ UseQueryResult,
8
+ useSuspenseQuery as useSuspenseQueryTanstack,
9
+ } from '@tanstack/react-query';
10
+
11
+ import type { QraftClientOptions } from '../qraftAPIClient.js';
12
+ import { QraftContext } from '../QraftContext.js';
13
+ import type { RequestClientSchema } from '../RequestClient.js';
14
+ import {
15
+ ServiceOperationQuery,
16
+ ServiceOperationQueryKey,
17
+ } from '../ServiceOperation.js';
18
+
19
+ export const useSuspenseQuery: <
20
+ TQueryFnData = unknown,
21
+ TError = DefaultError,
22
+ TData = TQueryFnData,
23
+ >(
24
+ qraftOptions: QraftClientOptions | undefined,
25
+ schema: RequestClientSchema,
26
+ args: Parameters<
27
+ ServiceOperationQuery<
28
+ RequestClientSchema,
29
+ unknown,
30
+ unknown
31
+ >['useSuspenseQuery']
32
+ >
33
+ ) => UseQueryResult<TData, TError> & {
34
+ queryKey: ServiceOperationQueryKey<RequestClientSchema, unknown>;
35
+ } = (qraftOptions, schema, args) => {
36
+ const [params, options, ...restArgs] = args;
37
+
38
+ const requestClient = useContext(qraftOptions?.context ?? QraftContext)
39
+ ?.requestClient;
40
+
41
+ if (!requestClient) throw new Error(`QraftContext.requestClient not found`);
42
+
43
+ const queryKey: ServiceOperationQueryKey<RequestClientSchema, unknown> = [
44
+ { url: schema.url },
45
+ params,
46
+ ];
47
+
48
+ return useSuspenseQueryTanstack(
49
+ {
50
+ ...options,
51
+ queryKey,
52
+ queryFn:
53
+ options?.queryFn ??
54
+ function ({ queryKey: [, queryParams], signal, meta }) {
55
+ return requestClient(schema, {
56
+ parameters: queryParams as never,
57
+ signal,
58
+ meta,
59
+ });
60
+ },
61
+ },
62
+ ...restArgs
63
+ ) as never;
64
+ };
@@ -0,0 +1,43 @@
1
+ import { composeMutationKey } from './composeMutationKey.js';
2
+
3
+ describe('composeMutationKey', () => {
4
+ it('should return mutation key with parameters', () => {
5
+ const schema = {
6
+ url: 'https://example.com',
7
+ method: 'get',
8
+ } as const;
9
+
10
+ const parameters = {
11
+ body: 'body',
12
+ requestBody: 'requestBody',
13
+ other: 'other',
14
+ };
15
+ const result = composeMutationKey(schema, parameters);
16
+
17
+ expect(result).toEqual([
18
+ {
19
+ url: schema.url,
20
+ method: schema.method,
21
+ },
22
+ {
23
+ other: 'other',
24
+ },
25
+ ]);
26
+ });
27
+
28
+ it('should return mutation key without parameters', () => {
29
+ const schema = {
30
+ url: 'https://example.com',
31
+ method: 'get',
32
+ } as const;
33
+
34
+ const result = composeMutationKey(schema, undefined);
35
+
36
+ expect(result).toEqual([
37
+ {
38
+ url: schema.url,
39
+ method: schema.method,
40
+ },
41
+ ]);
42
+ });
43
+ });
@@ -0,0 +1,50 @@
1
+ import type { RequestClientSchema } from '../RequestClient.js';
2
+ import { ServiceOperationMutationKey } from '../ServiceOperation.js';
3
+
4
+ /**
5
+ * Omit `body` or `requestBody` from mutation parameters if exists
6
+ * and return the rest of the parameters
7
+ */
8
+ export function composeMutationKey<
9
+ TSchema extends RequestClientSchema,
10
+ TParams = undefined,
11
+ >(
12
+ schema: TSchema,
13
+ parameters: undefined
14
+ ): ServiceOperationMutationKey<TSchema, undefined>;
15
+ export function composeMutationKey<
16
+ TSchema extends RequestClientSchema,
17
+ TParams,
18
+ >(
19
+ schema: TSchema,
20
+ parameters: TParams
21
+ ): ServiceOperationMutationKey<TSchema, TParams>;
22
+ export function composeMutationKey<
23
+ TSchema extends RequestClientSchema,
24
+ TParams = unknown,
25
+ >(schema: TSchema, parameters: TParams | undefined) {
26
+ return parameters === undefined
27
+ ? [{ url: schema.url, method: schema.method }]
28
+ : [
29
+ {
30
+ url: schema.url,
31
+ method: schema.method,
32
+ },
33
+ omitMutationPayload(parameters),
34
+ ];
35
+ }
36
+
37
+ export function omitMutationPayload<T>(params: T) {
38
+ if (!params || typeof params !== 'object')
39
+ throw new Error('`params` must be object');
40
+
41
+ if ('body' in params || 'requestBody' in params) {
42
+ const { body, requestBody, ...paramsRest } = params as Record<
43
+ 'body' | 'requestBody',
44
+ unknown
45
+ >;
46
+ return paramsRest;
47
+ }
48
+
49
+ return params as Omit<T, 'body' | 'requestBody'>;
50
+ }
@@ -1,4 +1,4 @@
1
- import { shelfMerge } from './useInfiniteQuery.js';
1
+ import { shelfMerge } from './shelfMerge.js';
2
2
 
3
3
  describe('shelfMerge', () => {
4
4
  it('merges simple objects', async () => {