@navios/react-query 0.7.1 → 1.0.0-alpha.1

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 (94) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/README.md +152 -4
  3. package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
  4. package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
  5. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
  6. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
  7. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
  8. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
  9. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
  10. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
  11. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
  12. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
  13. package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
  14. package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
  15. package/dist/src/client/declare-client.d.mts +15 -8
  16. package/dist/src/client/declare-client.d.mts.map +1 -1
  17. package/dist/src/client/types/from-endpoint.d.mts +130 -0
  18. package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
  19. package/dist/src/client/types/helpers.d.mts +74 -0
  20. package/dist/src/client/types/helpers.d.mts.map +1 -0
  21. package/dist/src/client/types/index.d.mts +21 -0
  22. package/dist/src/client/types/index.d.mts.map +1 -0
  23. package/dist/src/client/types/infinite-query.d.mts +61 -0
  24. package/dist/src/client/types/infinite-query.d.mts.map +1 -0
  25. package/dist/src/client/types/multipart-mutation.d.mts +98 -0
  26. package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
  27. package/dist/src/client/types/mutation.d.mts +75 -0
  28. package/dist/src/client/types/mutation.d.mts.map +1 -0
  29. package/dist/src/client/types/query.d.mts +65 -0
  30. package/dist/src/client/types/query.d.mts.map +1 -0
  31. package/dist/src/client/types.d.mts +1 -608
  32. package/dist/src/client/types.d.mts.map +1 -1
  33. package/dist/src/common/types.d.mts +30 -3
  34. package/dist/src/common/types.d.mts.map +1 -1
  35. package/dist/src/mutation/index.d.mts +1 -0
  36. package/dist/src/mutation/index.d.mts.map +1 -1
  37. package/dist/src/mutation/make-hook.d.mts +42 -16
  38. package/dist/src/mutation/make-hook.d.mts.map +1 -1
  39. package/dist/src/mutation/optimistic.d.mts +166 -0
  40. package/dist/src/mutation/optimistic.d.mts.map +1 -0
  41. package/dist/src/mutation/types.d.mts +51 -19
  42. package/dist/src/mutation/types.d.mts.map +1 -1
  43. package/dist/src/query/index.d.mts +1 -0
  44. package/dist/src/query/index.d.mts.map +1 -1
  45. package/dist/src/query/key-creator.d.mts.map +1 -1
  46. package/dist/src/query/make-infinite-options.d.mts +3 -2
  47. package/dist/src/query/make-infinite-options.d.mts.map +1 -1
  48. package/dist/src/query/make-options.d.mts +42 -12
  49. package/dist/src/query/make-options.d.mts.map +1 -1
  50. package/dist/src/query/prefetch.d.mts +245 -0
  51. package/dist/src/query/prefetch.d.mts.map +1 -0
  52. package/dist/src/query/types.d.mts +35 -17
  53. package/dist/src/query/types.d.mts.map +1 -1
  54. package/dist/tsconfig.tsbuildinfo +1 -1
  55. package/lib/index.cjs +445 -28
  56. package/lib/index.cjs.map +1 -1
  57. package/lib/index.d.cts +1022 -599
  58. package/lib/index.d.cts.map +1 -1
  59. package/lib/index.d.mts +1019 -596
  60. package/lib/index.d.mts.map +1 -1
  61. package/lib/index.mjs +441 -29
  62. package/lib/index.mjs.map +1 -1
  63. package/package.json +3 -3
  64. package/src/__tests__/declare-client.spec.mts +1 -2
  65. package/src/__tests__/errorSchema.spec.mts +391 -0
  66. package/src/__tests__/make-mutation.spec.mts +6 -5
  67. package/src/__tests__/makeDataTag.spec.mts +2 -1
  68. package/src/__tests__/makeQueryOptions.spec.mts +2 -1
  69. package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
  70. package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
  71. package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
  72. package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
  73. package/src/client/__type-tests__/query.spec-d.mts +701 -0
  74. package/src/client/declare-client.mts +59 -34
  75. package/src/client/types/from-endpoint.mts +345 -0
  76. package/src/client/types/helpers.mts +140 -0
  77. package/src/client/types/index.mts +26 -0
  78. package/src/client/types/infinite-query.mts +133 -0
  79. package/src/client/types/multipart-mutation.mts +264 -0
  80. package/src/client/types/mutation.mts +176 -0
  81. package/src/client/types/query.mts +132 -0
  82. package/src/client/types.mts +1 -1935
  83. package/src/common/types.mts +48 -3
  84. package/src/mutation/index.mts +1 -0
  85. package/src/mutation/make-hook.mts +171 -63
  86. package/src/mutation/optimistic.mts +294 -0
  87. package/src/mutation/types.mts +102 -29
  88. package/src/query/index.mts +1 -0
  89. package/src/query/key-creator.mts +24 -13
  90. package/src/query/make-infinite-options.mts +53 -10
  91. package/src/query/make-options.mts +184 -43
  92. package/src/query/prefetch.mts +326 -0
  93. package/src/query/types.mts +76 -16
  94. package/src/client/__type-tests__/client-instance.spec-d.mts +0 -852
@@ -0,0 +1,701 @@
1
+ import type { ErrorSchemaRecord } from '@navios/builder'
2
+ import type {
3
+ DataTag,
4
+ UseSuspenseQueryOptions,
5
+ } from '@tanstack/react-query'
6
+ import type { z } from 'zod/v4'
7
+
8
+ import { assertType, describe, test } from 'vitest'
9
+ import { z as zod } from 'zod/v4'
10
+
11
+ import type { Split } from '../../common/types.mjs'
12
+ import type { QueryHelpers } from '../../query/types.mjs'
13
+ import type { ClientInstance, EndpointHelper } from '../types.mjs'
14
+
15
+ // ============================================================================
16
+ // TEST SCHEMAS
17
+ // ============================================================================
18
+
19
+ const responseSchema = zod.object({
20
+ id: zod.string(),
21
+ name: zod.string(),
22
+ })
23
+
24
+ const querySchema = zod.object({
25
+ page: zod.number(),
26
+ limit: zod.number(),
27
+ })
28
+
29
+ const requestSchema = zod.object({
30
+ name: zod.string(),
31
+ email: zod.string(),
32
+ })
33
+
34
+ const error400Schema = zod.object({ error: zod.string(), code: zod.number() })
35
+ const error404Schema = zod.object({ notFound: zod.literal(true) })
36
+ const error500Schema = zod.object({ serverError: zod.string() })
37
+
38
+ const errorSchema = {
39
+ 400: error400Schema,
40
+ 404: error404Schema,
41
+ 500: error500Schema,
42
+ } satisfies ErrorSchemaRecord
43
+
44
+ type ResponseType = z.output<typeof responseSchema>
45
+ type QueryType = z.input<typeof querySchema>
46
+ type RequestType = z.input<typeof requestSchema>
47
+ type Error400 = z.output<typeof error400Schema>
48
+ type Error404 = z.output<typeof error404Schema>
49
+ type Error500 = z.output<typeof error500Schema>
50
+ type ErrorUnion = Error400 | Error404 | Error500
51
+ type ResponseWithErrors = ResponseType | ErrorUnion
52
+
53
+ // ============================================================================
54
+ // CLIENT INSTANCE DECLARATIONS
55
+ // ============================================================================
56
+
57
+ declare const client: ClientInstance<false>
58
+ declare const clientWithDiscriminator: ClientInstance<true>
59
+
60
+ // ============================================================================
61
+ // QUERY METHOD - DEFAULT MODE (UseDiscriminator=false)
62
+ // ============================================================================
63
+
64
+ describe('ClientInstance<false> query() method', () => {
65
+ describe('GET endpoints', () => {
66
+ test('simple GET query without params', () => {
67
+ const query = client.query({
68
+ method: 'GET',
69
+ url: '/users',
70
+ responseSchema,
71
+ })
72
+
73
+ assertType<
74
+ (params: {}) => UseSuspenseQueryOptions<
75
+ ResponseType,
76
+ Error,
77
+ ResponseType,
78
+ DataTag<Split<'/users', '/'>, ResponseType, Error>
79
+ >
80
+ >(query)
81
+
82
+ assertType<QueryHelpers<'/users', undefined, ResponseType>['queryKey']>(
83
+ query.queryKey,
84
+ )
85
+ assertType<QueryHelpers<'/users', undefined, ResponseType>['use']>(
86
+ query.use,
87
+ )
88
+ assertType<
89
+ QueryHelpers<'/users', undefined, ResponseType>['useSuspense']
90
+ >(query.useSuspense)
91
+ assertType<
92
+ QueryHelpers<'/users', undefined, ResponseType>['invalidate']
93
+ >(query.invalidate)
94
+ assertType<
95
+ QueryHelpers<'/users', undefined, ResponseType>['invalidateAll']
96
+ >(query.invalidateAll)
97
+ })
98
+
99
+ test('GET query with single URL param', () => {
100
+ const query = client.query({
101
+ method: 'GET',
102
+ url: '/users/$userId',
103
+ responseSchema,
104
+ })
105
+
106
+ assertType<
107
+ (params: {
108
+ urlParams: { userId: string | number }
109
+ }) => UseSuspenseQueryOptions<
110
+ ResponseType,
111
+ Error,
112
+ ResponseType,
113
+ DataTag<Split<'/users/$userId', '/'>, ResponseType, Error>
114
+ >
115
+ >(query)
116
+ })
117
+
118
+ test('GET query with multiple URL params', () => {
119
+ const query = client.query({
120
+ method: 'GET',
121
+ url: '/orgs/$orgId/users/$userId',
122
+ responseSchema,
123
+ })
124
+
125
+ assertType<
126
+ (params: {
127
+ urlParams: { orgId: string | number; userId: string | number }
128
+ }) => UseSuspenseQueryOptions<
129
+ ResponseType,
130
+ Error,
131
+ ResponseType,
132
+ DataTag<Split<'/orgs/$orgId/users/$userId', '/'>, ResponseType, Error>
133
+ >
134
+ >(query)
135
+ })
136
+
137
+ test('GET query with query schema', () => {
138
+ const query = client.query({
139
+ method: 'GET',
140
+ url: '/users',
141
+ querySchema,
142
+ responseSchema,
143
+ })
144
+
145
+ assertType<
146
+ (params: {
147
+ params: QueryType
148
+ }) => UseSuspenseQueryOptions<
149
+ ResponseType,
150
+ Error,
151
+ ResponseType,
152
+ DataTag<Split<'/users', '/'>, ResponseType, Error>
153
+ >
154
+ >(query)
155
+
156
+ assertType<
157
+ QueryHelpers<'/users', typeof querySchema, ResponseType>['queryKey']
158
+ >(query.queryKey)
159
+ })
160
+
161
+ test('GET query with URL params and query schema', () => {
162
+ const query = client.query({
163
+ method: 'GET',
164
+ url: '/users/$userId/posts',
165
+ querySchema,
166
+ responseSchema,
167
+ })
168
+
169
+ assertType<
170
+ (params: {
171
+ urlParams: { userId: string | number }
172
+ params: QueryType
173
+ }) => UseSuspenseQueryOptions<
174
+ ResponseType,
175
+ Error,
176
+ ResponseType,
177
+ DataTag<Split<'/users/$userId/posts', '/'>, ResponseType, Error>
178
+ >
179
+ >(query)
180
+ })
181
+
182
+ test('GET query with processResponse transformation', () => {
183
+ const query = client.query({
184
+ method: 'GET',
185
+ url: '/users',
186
+ responseSchema,
187
+ processResponse: (data) => data.name.toUpperCase(),
188
+ })
189
+
190
+ assertType<
191
+ (params: {}) => UseSuspenseQueryOptions<
192
+ string,
193
+ Error,
194
+ string,
195
+ DataTag<Split<'/users', '/'>, string, Error>
196
+ >
197
+ >(query)
198
+ })
199
+
200
+ test('GET query with processResponse returning object', () => {
201
+ // Note: processResponse infers const types, so we check the shape
202
+ const query = client.query({
203
+ method: 'GET',
204
+ url: '/users',
205
+ responseSchema,
206
+ processResponse: (data): { processed: boolean; user: ResponseType } => ({
207
+ processed: true,
208
+ user: data,
209
+ }),
210
+ })
211
+
212
+ assertType<
213
+ (params: {}) => UseSuspenseQueryOptions<
214
+ { processed: boolean; user: ResponseType },
215
+ Error,
216
+ { processed: boolean; user: ResponseType },
217
+ DataTag<
218
+ Split<'/users', '/'>,
219
+ { processed: boolean; user: ResponseType },
220
+ Error
221
+ >
222
+ >
223
+ >(query)
224
+ })
225
+
226
+ test('GET query with errorSchema (errors thrown, not in return type)', () => {
227
+ const query = client.query({
228
+ method: 'GET',
229
+ url: '/users',
230
+ responseSchema,
231
+ errorSchema,
232
+ })
233
+
234
+ // With UseDiscriminator=false, errors are thrown, not returned
235
+ assertType<
236
+ (params: {}) => UseSuspenseQueryOptions<
237
+ ResponseType,
238
+ Error,
239
+ ResponseType,
240
+ DataTag<Split<'/users', '/'>, ResponseType, Error>
241
+ >
242
+ >(query)
243
+ })
244
+ })
245
+
246
+ describe('HEAD and OPTIONS endpoints', () => {
247
+ test('HEAD query', () => {
248
+ const query = client.query({
249
+ method: 'HEAD',
250
+ url: '/ping',
251
+ responseSchema,
252
+ })
253
+
254
+ assertType<
255
+ (params: {}) => UseSuspenseQueryOptions<
256
+ ResponseType,
257
+ Error,
258
+ ResponseType,
259
+ DataTag<Split<'/ping', '/'>, ResponseType, Error>
260
+ >
261
+ >(query)
262
+ })
263
+
264
+ test('OPTIONS query', () => {
265
+ const query = client.query({
266
+ method: 'OPTIONS',
267
+ url: '/cors',
268
+ responseSchema,
269
+ })
270
+
271
+ assertType<
272
+ (params: {}) => UseSuspenseQueryOptions<
273
+ ResponseType,
274
+ Error,
275
+ ResponseType,
276
+ DataTag<Split<'/cors', '/'>, ResponseType, Error>
277
+ >
278
+ >(query)
279
+ })
280
+ })
281
+
282
+ describe('POST query endpoints (for search)', () => {
283
+ test('POST query with request schema', () => {
284
+ const query = client.query({
285
+ method: 'POST',
286
+ url: '/search',
287
+ requestSchema,
288
+ responseSchema,
289
+ })
290
+
291
+ assertType<
292
+ (params: {
293
+ data: RequestType
294
+ }) => UseSuspenseQueryOptions<
295
+ ResponseType,
296
+ Error,
297
+ ResponseType,
298
+ DataTag<Split<'/search', '/'>, ResponseType, Error>
299
+ >
300
+ >(query)
301
+ })
302
+
303
+ test('POST query with URL params and request schema', () => {
304
+ const query = client.query({
305
+ method: 'POST',
306
+ url: '/users/$userId/search',
307
+ requestSchema,
308
+ responseSchema,
309
+ })
310
+
311
+ assertType<
312
+ (params: {
313
+ urlParams: { userId: string | number }
314
+ data: RequestType
315
+ }) => UseSuspenseQueryOptions<
316
+ ResponseType,
317
+ Error,
318
+ ResponseType,
319
+ DataTag<Split<'/users/$userId/search', '/'>, ResponseType, Error>
320
+ >
321
+ >(query)
322
+ })
323
+
324
+ test('POST query with all schemas', () => {
325
+ const query = client.query({
326
+ method: 'POST',
327
+ url: '/search',
328
+ querySchema,
329
+ requestSchema,
330
+ responseSchema,
331
+ })
332
+
333
+ assertType<
334
+ (params: {
335
+ params: QueryType
336
+ data: RequestType
337
+ }) => UseSuspenseQueryOptions<
338
+ ResponseType,
339
+ Error,
340
+ ResponseType,
341
+ DataTag<Split<'/search', '/'>, ResponseType, Error>
342
+ >
343
+ >(query)
344
+ })
345
+
346
+ test('POST query with URL params, query schema, and request schema', () => {
347
+ const query = client.query({
348
+ method: 'POST',
349
+ url: '/orgs/$orgId/search',
350
+ querySchema,
351
+ requestSchema,
352
+ responseSchema,
353
+ })
354
+
355
+ assertType<
356
+ (params: {
357
+ urlParams: { orgId: string | number }
358
+ params: QueryType
359
+ data: RequestType
360
+ }) => UseSuspenseQueryOptions<
361
+ ResponseType,
362
+ Error,
363
+ ResponseType,
364
+ DataTag<Split<'/orgs/$orgId/search', '/'>, ResponseType, Error>
365
+ >
366
+ >(query)
367
+ })
368
+ })
369
+
370
+ describe('EndpointHelper', () => {
371
+ test('query exposes endpoint property', () => {
372
+ const query = client.query({
373
+ method: 'GET',
374
+ url: '/users',
375
+ responseSchema,
376
+ })
377
+
378
+ // EndpointHelper only includes properties that were actually provided
379
+ // (no explicit undefined for missing optional properties)
380
+ assertType<
381
+ EndpointHelper<
382
+ {
383
+ method: 'GET'
384
+ url: '/users'
385
+ responseSchema: typeof responseSchema
386
+ },
387
+ false
388
+ >['endpoint']
389
+ >(query.endpoint)
390
+ })
391
+
392
+ test('query with all schemas exposes endpoint', () => {
393
+ const query = client.query({
394
+ method: 'POST',
395
+ url: '/search',
396
+ querySchema,
397
+ requestSchema,
398
+ responseSchema,
399
+ errorSchema,
400
+ })
401
+
402
+ // EndpointHelper only includes properties that were actually provided
403
+ assertType<
404
+ EndpointHelper<
405
+ {
406
+ method: 'POST'
407
+ url: '/search'
408
+ querySchema: typeof querySchema
409
+ requestSchema: typeof requestSchema
410
+ responseSchema: typeof responseSchema
411
+ errorSchema: typeof errorSchema
412
+ },
413
+ false
414
+ >['endpoint']
415
+ >(query.endpoint)
416
+ })
417
+ })
418
+ })
419
+
420
+ // ============================================================================
421
+ // QUERY METHOD - DISCRIMINATOR MODE (UseDiscriminator=true)
422
+ // ============================================================================
423
+
424
+ describe('ClientInstance<true> query() method (discriminator mode)', () => {
425
+ describe('errorSchema includes error union in TData', () => {
426
+ test('GET query with errorSchema returns union result type', () => {
427
+ const query = clientWithDiscriminator.query({
428
+ method: 'GET',
429
+ url: '/users',
430
+ responseSchema,
431
+ errorSchema,
432
+ })
433
+
434
+ assertType<
435
+ (params: {}) => UseSuspenseQueryOptions<
436
+ ResponseWithErrors,
437
+ Error,
438
+ ResponseWithErrors,
439
+ DataTag<Split<'/users', '/'>, ResponseWithErrors, Error>
440
+ >
441
+ >(query)
442
+ })
443
+
444
+ test('GET query with errorSchema and URL params returns union', () => {
445
+ const query = clientWithDiscriminator.query({
446
+ method: 'GET',
447
+ url: '/users/$userId',
448
+ responseSchema,
449
+ errorSchema,
450
+ })
451
+
452
+ assertType<
453
+ (params: {
454
+ urlParams: { userId: string | number }
455
+ }) => UseSuspenseQueryOptions<
456
+ ResponseWithErrors,
457
+ Error,
458
+ ResponseWithErrors,
459
+ DataTag<Split<'/users/$userId', '/'>, ResponseWithErrors, Error>
460
+ >
461
+ >(query)
462
+ })
463
+
464
+ test('GET query with errorSchema and query schema returns union', () => {
465
+ const query = clientWithDiscriminator.query({
466
+ method: 'GET',
467
+ url: '/users',
468
+ querySchema,
469
+ responseSchema,
470
+ errorSchema,
471
+ })
472
+
473
+ assertType<
474
+ (params: {
475
+ params: QueryType
476
+ }) => UseSuspenseQueryOptions<
477
+ ResponseWithErrors,
478
+ Error,
479
+ ResponseWithErrors,
480
+ DataTag<Split<'/users', '/'>, ResponseWithErrors, Error>
481
+ >
482
+ >(query)
483
+ })
484
+
485
+ test('processResponse receives union type', () => {
486
+ clientWithDiscriminator.query({
487
+ method: 'GET',
488
+ url: '/users',
489
+ responseSchema,
490
+ errorSchema,
491
+ processResponse: (data) => {
492
+ assertType<ResponseWithErrors>(data)
493
+ return data
494
+ },
495
+ })
496
+ })
497
+
498
+ test('processResponse can transform union type', () => {
499
+ // Note: This test verifies processResponse receives the union type
500
+ // and can return a transformed type. The exact type assertion is
501
+ // complex due to readonly inference, so we just verify the callback types.
502
+ clientWithDiscriminator.query({
503
+ method: 'GET',
504
+ url: '/users',
505
+ responseSchema,
506
+ errorSchema,
507
+ processResponse: (data): { handled: boolean; original: typeof data } => {
508
+ assertType<ResponseWithErrors>(data)
509
+ return { handled: true, original: data }
510
+ },
511
+ })
512
+ })
513
+ })
514
+
515
+ describe('without errorSchema behaves same as default', () => {
516
+ test('GET query without errorSchema returns only success type', () => {
517
+ const query = clientWithDiscriminator.query({
518
+ method: 'GET',
519
+ url: '/users',
520
+ responseSchema,
521
+ })
522
+
523
+ assertType<
524
+ (params: {}) => UseSuspenseQueryOptions<
525
+ ResponseType,
526
+ Error,
527
+ ResponseType,
528
+ DataTag<Split<'/users', '/'>, ResponseType, Error>
529
+ >
530
+ >(query)
531
+ })
532
+ })
533
+ })
534
+
535
+ // ============================================================================
536
+ // ERROR CASES - Should fail type checking
537
+ // ============================================================================
538
+
539
+ describe('query() error cases', () => {
540
+ describe('missing required parameters', () => {
541
+ test('GET query without urlParams when URL has params', () => {
542
+ const query = client.query({
543
+ method: 'GET',
544
+ url: '/users/$userId',
545
+ responseSchema,
546
+ })
547
+
548
+ // @ts-expect-error - missing urlParams
549
+ query({})
550
+ })
551
+
552
+ test('GET query without params when querySchema is defined', () => {
553
+ const query = client.query({
554
+ method: 'GET',
555
+ url: '/users',
556
+ querySchema,
557
+ responseSchema,
558
+ })
559
+
560
+ // @ts-expect-error - missing params
561
+ query({})
562
+ })
563
+
564
+ test('POST query without data when requestSchema is defined', () => {
565
+ const query = client.query({
566
+ method: 'POST',
567
+ url: '/search',
568
+ requestSchema,
569
+ responseSchema,
570
+ })
571
+
572
+ // @ts-expect-error - missing data
573
+ query({})
574
+ })
575
+
576
+ test('GET query with URL params - missing one param', () => {
577
+ const query = client.query({
578
+ method: 'GET',
579
+ url: '/orgs/$orgId/users/$userId',
580
+ responseSchema,
581
+ })
582
+
583
+ // @ts-expect-error - missing userId
584
+ query({ urlParams: { orgId: '123' } })
585
+ })
586
+
587
+ test('POST query with all schemas - missing data', () => {
588
+ const query = client.query({
589
+ method: 'POST',
590
+ url: '/search',
591
+ querySchema,
592
+ requestSchema,
593
+ responseSchema,
594
+ })
595
+
596
+ // @ts-expect-error - missing data
597
+ query({ params: { page: 1, limit: 10 } })
598
+ })
599
+
600
+ test('POST query with all schemas - missing params', () => {
601
+ const query = client.query({
602
+ method: 'POST',
603
+ url: '/search',
604
+ querySchema,
605
+ requestSchema,
606
+ responseSchema,
607
+ })
608
+
609
+ // @ts-expect-error - missing params
610
+ query({ data: { name: 'test', email: 'test@test.com' } })
611
+ })
612
+ })
613
+
614
+ describe('wrong parameter types', () => {
615
+ test('urlParams with wrong type', () => {
616
+ const query = client.query({
617
+ method: 'GET',
618
+ url: '/users/$userId',
619
+ responseSchema,
620
+ })
621
+
622
+ // @ts-expect-error - userId should be string | number, not boolean
623
+ query({ urlParams: { userId: true } })
624
+ })
625
+
626
+ test('params with wrong shape', () => {
627
+ const query = client.query({
628
+ method: 'GET',
629
+ url: '/users',
630
+ querySchema,
631
+ responseSchema,
632
+ })
633
+
634
+ // @ts-expect-error - wrong property names
635
+ query({ params: { offset: 0, count: 10 } })
636
+ })
637
+
638
+ test('params with wrong value types', () => {
639
+ const query = client.query({
640
+ method: 'GET',
641
+ url: '/users',
642
+ querySchema,
643
+ responseSchema,
644
+ })
645
+
646
+ // @ts-expect-error - page should be number, not string
647
+ query({ params: { page: '1', limit: 10 } })
648
+ })
649
+
650
+ test('data with wrong shape', () => {
651
+ const query = client.query({
652
+ method: 'POST',
653
+ url: '/search',
654
+ requestSchema,
655
+ responseSchema,
656
+ })
657
+
658
+ // @ts-expect-error - wrong property names
659
+ query({ data: { username: 'test', mail: 'test@test.com' } })
660
+ })
661
+
662
+ test('data with missing required fields', () => {
663
+ const query = client.query({
664
+ method: 'POST',
665
+ url: '/search',
666
+ requestSchema,
667
+ responseSchema,
668
+ })
669
+
670
+ // @ts-expect-error - missing email
671
+ query({ data: { name: 'test' } })
672
+ })
673
+ })
674
+
675
+ describe('extra/unknown parameters', () => {
676
+ test('extra urlParams should fail', () => {
677
+ const query = client.query({
678
+ method: 'GET',
679
+ url: '/users/$userId',
680
+ responseSchema,
681
+ })
682
+
683
+ // @ts-expect-error - extraParam not in URL
684
+ query({ urlParams: { userId: '123', extraParam: 'invalid' } })
685
+ })
686
+ })
687
+
688
+ describe('processResponse type safety', () => {
689
+ test('processResponse receives correct input type', () => {
690
+ client.query({
691
+ method: 'GET',
692
+ url: '/users',
693
+ responseSchema,
694
+ processResponse: (data) => {
695
+ // @ts-expect-error - data doesn't have 'nonExistent' property
696
+ return data.nonExistent
697
+ },
698
+ })
699
+ })
700
+ })
701
+ })