@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.
- package/CHANGELOG.md +161 -1
- package/README.md +152 -4
- package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
- package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
- package/dist/src/client/declare-client.d.mts +15 -8
- package/dist/src/client/declare-client.d.mts.map +1 -1
- package/dist/src/client/types/from-endpoint.d.mts +130 -0
- package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
- package/dist/src/client/types/helpers.d.mts +74 -0
- package/dist/src/client/types/helpers.d.mts.map +1 -0
- package/dist/src/client/types/index.d.mts +21 -0
- package/dist/src/client/types/index.d.mts.map +1 -0
- package/dist/src/client/types/infinite-query.d.mts +61 -0
- package/dist/src/client/types/infinite-query.d.mts.map +1 -0
- package/dist/src/client/types/multipart-mutation.d.mts +98 -0
- package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
- package/dist/src/client/types/mutation.d.mts +75 -0
- package/dist/src/client/types/mutation.d.mts.map +1 -0
- package/dist/src/client/types/query.d.mts +65 -0
- package/dist/src/client/types/query.d.mts.map +1 -0
- package/dist/src/client/types.d.mts +1 -608
- package/dist/src/client/types.d.mts.map +1 -1
- package/dist/src/common/types.d.mts +30 -3
- package/dist/src/common/types.d.mts.map +1 -1
- package/dist/src/mutation/index.d.mts +1 -0
- package/dist/src/mutation/index.d.mts.map +1 -1
- package/dist/src/mutation/make-hook.d.mts +42 -16
- package/dist/src/mutation/make-hook.d.mts.map +1 -1
- package/dist/src/mutation/optimistic.d.mts +166 -0
- package/dist/src/mutation/optimistic.d.mts.map +1 -0
- package/dist/src/mutation/types.d.mts +51 -19
- package/dist/src/mutation/types.d.mts.map +1 -1
- package/dist/src/query/index.d.mts +1 -0
- package/dist/src/query/index.d.mts.map +1 -1
- package/dist/src/query/key-creator.d.mts.map +1 -1
- package/dist/src/query/make-infinite-options.d.mts +3 -2
- package/dist/src/query/make-infinite-options.d.mts.map +1 -1
- package/dist/src/query/make-options.d.mts +42 -12
- package/dist/src/query/make-options.d.mts.map +1 -1
- package/dist/src/query/prefetch.d.mts +245 -0
- package/dist/src/query/prefetch.d.mts.map +1 -0
- package/dist/src/query/types.d.mts +35 -17
- package/dist/src/query/types.d.mts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/index.cjs +445 -28
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +1022 -599
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +1019 -596
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +441 -29
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/declare-client.spec.mts +1 -2
- package/src/__tests__/errorSchema.spec.mts +391 -0
- package/src/__tests__/make-mutation.spec.mts +6 -5
- package/src/__tests__/makeDataTag.spec.mts +2 -1
- package/src/__tests__/makeQueryOptions.spec.mts +2 -1
- package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
- package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
- package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
- package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
- package/src/client/__type-tests__/query.spec-d.mts +701 -0
- package/src/client/declare-client.mts +59 -34
- package/src/client/types/from-endpoint.mts +345 -0
- package/src/client/types/helpers.mts +140 -0
- package/src/client/types/index.mts +26 -0
- package/src/client/types/infinite-query.mts +133 -0
- package/src/client/types/multipart-mutation.mts +264 -0
- package/src/client/types/mutation.mts +176 -0
- package/src/client/types/query.mts +132 -0
- package/src/client/types.mts +1 -1935
- package/src/common/types.mts +48 -3
- package/src/mutation/index.mts +1 -0
- package/src/mutation/make-hook.mts +171 -63
- package/src/mutation/optimistic.mts +294 -0
- package/src/mutation/types.mts +102 -29
- package/src/query/index.mts +1 -0
- package/src/query/key-creator.mts +24 -13
- package/src/query/make-infinite-options.mts +53 -10
- package/src/query/make-options.mts +184 -43
- package/src/query/prefetch.mts +326 -0
- package/src/query/types.mts +76 -16
- 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
|
+
})
|