@geekmidas/client 9.0.1 → 10.0.0-alpha.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 (81) hide show
  1. package/dist/auth-fetcher-CBYuL6rx.d.cts +158 -0
  2. package/dist/auth-fetcher-CBYuL6rx.d.cts.map +1 -0
  3. package/dist/auth-fetcher-DOj0iz21.mjs +77 -0
  4. package/dist/auth-fetcher-DOj0iz21.mjs.map +1 -0
  5. package/dist/auth-fetcher-DxS8zOLc.d.mts +158 -0
  6. package/dist/auth-fetcher-DxS8zOLc.d.mts.map +1 -0
  7. package/dist/auth-fetcher-HILABmws.cjs +82 -0
  8. package/dist/auth-fetcher-HILABmws.cjs.map +1 -0
  9. package/dist/auth-fetcher.cjs +3 -76
  10. package/dist/auth-fetcher.d.cts +3 -158
  11. package/dist/auth-fetcher.d.mts +3 -158
  12. package/dist/auth-fetcher.mjs +3 -76
  13. package/dist/endpoint-hooks.d.mts +1 -1
  14. package/dist/fetcher-C1xtCKpT.d.mts +25 -0
  15. package/dist/fetcher-C1xtCKpT.d.mts.map +1 -0
  16. package/dist/fetcher-cDrfwN-Y.d.cts +25 -0
  17. package/dist/fetcher-cDrfwN-Y.d.cts.map +1 -0
  18. package/dist/fetcher.d.cts +3 -25
  19. package/dist/fetcher.d.mts +3 -25
  20. package/dist/index.cjs +8 -0
  21. package/dist/index.d.cts +5 -0
  22. package/dist/index.d.mts +5 -0
  23. package/dist/index.mjs +6 -0
  24. package/dist/infer-6KKCgmg1.cjs +0 -0
  25. package/{src/infer.ts → dist/infer-DfRq5kIb.d.cts} +54 -83
  26. package/dist/infer-DfRq5kIb.d.cts.map +1 -0
  27. package/dist/infer-bTirNeNW.d.mts +133 -0
  28. package/dist/infer-bTirNeNW.d.mts.map +1 -0
  29. package/dist/infer-uCUq1whM.mjs +0 -0
  30. package/dist/infer.cjs +1 -0
  31. package/dist/infer.d.cts +2 -133
  32. package/dist/infer.d.mts +2 -133
  33. package/dist/infer.mjs +1 -0
  34. package/dist/openapi-hooks.cjs.map +1 -1
  35. package/dist/openapi-hooks.d.cts +3 -3
  36. package/dist/openapi-hooks.d.cts.map +1 -1
  37. package/dist/openapi-hooks.d.mts +1 -1
  38. package/dist/openapi-hooks.d.mts.map +1 -1
  39. package/dist/openapi-hooks.mjs.map +1 -1
  40. package/dist/react-query.d.cts +7 -7
  41. package/dist/react-query.d.mts +1 -1
  42. package/dist/types-BH8WgGrm.mjs +0 -0
  43. package/dist/{types-D8Jrl4o3.d.mts → types-BMGM6sZc.d.mts} +1 -1
  44. package/dist/{types-D8Jrl4o3.d.mts.map → types-BMGM6sZc.d.mts.map} +1 -1
  45. package/dist/types-a8gm_IaQ.cjs +0 -0
  46. package/dist/types.cjs +1 -0
  47. package/dist/types.d.mts +1 -1
  48. package/dist/types.mjs +1 -0
  49. package/package.json +8 -5
  50. package/CHANGELOG.md +0 -124
  51. package/dist/auth-fetcher.cjs.map +0 -1
  52. package/dist/auth-fetcher.d.cts.map +0 -1
  53. package/dist/auth-fetcher.d.mts.map +0 -1
  54. package/dist/auth-fetcher.mjs.map +0 -1
  55. package/dist/fetcher.d.cts.map +0 -1
  56. package/dist/fetcher.d.mts.map +0 -1
  57. package/dist/infer.d.cts.map +0 -1
  58. package/dist/infer.d.mts.map +0 -1
  59. package/src/__tests__/auth-fetcher.spec.ts +0 -505
  60. package/src/__tests__/endpoint-hooks.spec.tsx +0 -374
  61. package/src/__tests__/fetcher.spec.ts +0 -580
  62. package/src/__tests__/infer.integration.spec.ts +0 -188
  63. package/src/__tests__/infer.spec.ts +0 -189
  64. package/src/__tests__/method-restrictions.spec.tsx +0 -227
  65. package/src/__tests__/mutation-inference.spec.tsx +0 -343
  66. package/src/__tests__/openapi-hooks.spec.tsx +0 -557
  67. package/src/__tests__/react-query-infinite.spec.tsx +0 -1003
  68. package/src/__tests__/react-query-invalidation.spec.tsx +0 -239
  69. package/src/__tests__/react-query.spec.tsx +0 -582
  70. package/src/__tests__/setup.ts +0 -281
  71. package/src/__tests__/types.spec.ts +0 -134
  72. package/src/__tests__/url-parsing.spec.ts +0 -196
  73. package/src/auth-fetcher.ts +0 -288
  74. package/src/endpoint-hooks.ts +0 -346
  75. package/src/fetcher.ts +0 -171
  76. package/src/openapi-hooks.ts +0 -193
  77. package/src/openapi-types.d.ts +0 -440
  78. package/src/openapi.json +0 -595
  79. package/src/react-query.ts +0 -341
  80. package/src/types.ts +0 -226
  81. package/tsconfig.json +0 -12
@@ -1,582 +0,0 @@
1
- /**
2
- * @vitest-environment jsdom
3
- */
4
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
- import { act, renderHook, waitFor } from '@testing-library/react';
6
- import { HttpResponse, http } from 'msw';
7
- // biome-ignore lint/style/useImportType: <explanation>
8
- import React from 'react';
9
- import { describe, expect, it } from 'vitest';
10
- import type { paths } from '../openapi-types';
11
- import {
12
- createTypedQueryClient,
13
- useTypedMutation,
14
- useTypedQuery,
15
- } from '../react-query';
16
- import './setup';
17
-
18
- describe('TypedQueryClient', () => {
19
- const queryClient = new QueryClient({
20
- defaultOptions: {
21
- queries: {
22
- retry: false,
23
- },
24
- },
25
- });
26
-
27
- const wrapper = ({ children }: { children: React.ReactNode }) => (
28
- <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
29
- );
30
-
31
- it('should create query client with correct configuration', () => {
32
- const typedClient = createTypedQueryClient<paths>({
33
- baseURL: 'https://api.example.com',
34
- headers: { Authorization: 'Bearer token' },
35
- });
36
-
37
- expect(typedClient).toBeDefined();
38
- });
39
-
40
- describe('useQuery', () => {
41
- it('should fetch data successfully', async () => {
42
- const typedClient = createTypedQueryClient<paths>({
43
- baseURL: 'https://api.example.com',
44
- });
45
-
46
- const { result } = renderHook(
47
- () =>
48
- typedClient.useQuery('GET /users/{id}', {
49
- params: { id: '123' },
50
- }),
51
- { wrapper },
52
- );
53
-
54
- expect(result.current.isLoading).toBe(true);
55
-
56
- await waitFor(() => {
57
- expect(result.current.isSuccess).toBe(true);
58
- });
59
-
60
- expect(result.current.data).toEqual({
61
- id: '123',
62
- name: 'John Doe',
63
- email: 'john@example.com',
64
- });
65
- });
66
-
67
- it('should handle query parameters', async () => {
68
- const typedClient = createTypedQueryClient<paths>({
69
- baseURL: 'https://api.example.com',
70
- });
71
-
72
- const { result } = renderHook(
73
- () =>
74
- typedClient.useQuery('GET /posts', {
75
- query: { page: 1, limit: 10, sort: 'desc' },
76
- }),
77
- { wrapper },
78
- );
79
-
80
- await waitFor(() => {
81
- expect(result.current.isSuccess).toBe(true);
82
- });
83
-
84
- expect(result.current.data).toMatchObject({
85
- posts: expect.any(Array),
86
- pagination: {
87
- page: 1,
88
- limit: 10,
89
- total: 50,
90
- },
91
- sort: 'desc',
92
- });
93
- });
94
-
95
- it('should handle errors', async () => {
96
- const typedClient = createTypedQueryClient<paths>({
97
- baseURL: 'https://api.example.com',
98
- });
99
-
100
- const { result } = renderHook(() => typedClient.useQuery('GET /error'), {
101
- wrapper,
102
- });
103
-
104
- await waitFor(() => {
105
- expect(result.current.isError).toBe(true);
106
- });
107
-
108
- expect(result.current.error).toBeDefined();
109
- });
110
-
111
- it('should pass additional query options', async () => {
112
- const typedClient = createTypedQueryClient<paths>({
113
- baseURL: 'https://api.example.com',
114
- });
115
-
116
- const { result } = renderHook(
117
- () =>
118
- typedClient.useQuery(
119
- 'GET /users/{id}',
120
- { params: { id: '123' } },
121
- {
122
- staleTime: 5 * 60 * 1000,
123
- gcTime: 10 * 60 * 1000,
124
- },
125
- ),
126
- { wrapper },
127
- );
128
-
129
- await waitFor(() => {
130
- expect(result.current.isSuccess).toBe(true);
131
- });
132
-
133
- expect(result.current.data).toBeDefined();
134
- });
135
- });
136
-
137
- describe('useMutation', () => {
138
- it('should perform mutations successfully', async () => {
139
- const typedClient = createTypedQueryClient<paths>({
140
- baseURL: 'https://api.example.com',
141
- });
142
-
143
- const { result } = renderHook(
144
- () => typedClient.useMutation('POST /users'),
145
- { wrapper },
146
- );
147
-
148
- expect(result.current.isPending).toBe(false);
149
-
150
- act(() => {
151
- result.current.mutate({
152
- body: {
153
- name: 'New User',
154
- email: 'new@example.com',
155
- },
156
- });
157
- });
158
-
159
- await waitFor(() => {
160
- expect(result.current.isSuccess).toBe(true);
161
- });
162
-
163
- expect(result.current.data).toEqual({
164
- id: '123',
165
- name: 'New User',
166
- email: 'new@example.com',
167
- });
168
- });
169
-
170
- it('should handle mutation with path parameters', async () => {
171
- const typedClient = createTypedQueryClient<paths>({
172
- baseURL: 'https://api.example.com',
173
- });
174
-
175
- const { result } = renderHook(
176
- () => typedClient.useMutation('PUT /users/{id}'),
177
- { wrapper },
178
- );
179
-
180
- act(() => {
181
- result.current.mutate({
182
- params: { id: '456' },
183
- body: {
184
- name: 'Updated User',
185
- },
186
- });
187
- });
188
-
189
- await waitFor(() => {
190
- expect(result.current.isSuccess).toBe(true);
191
- });
192
-
193
- expect(result.current.data).toMatchObject({
194
- id: '456',
195
- name: 'Updated User',
196
- });
197
- });
198
-
199
- it('should accept mutation options', async () => {
200
- const typedClient = createTypedQueryClient<paths>({
201
- baseURL: 'https://api.example.com',
202
- });
203
-
204
- let successData: any;
205
- const onSuccess = (data: any) => {
206
- successData = data;
207
- };
208
-
209
- const { result } = renderHook(
210
- () => typedClient.useMutation('POST /users', { onSuccess }),
211
- { wrapper },
212
- );
213
-
214
- act(() => {
215
- result.current.mutate({
216
- body: {
217
- name: 'Test User',
218
- email: 'test@example.com',
219
- },
220
- });
221
- });
222
-
223
- await waitFor(() => {
224
- expect(result.current.isSuccess).toBe(true);
225
- });
226
-
227
- expect(successData).toEqual({
228
- id: '123',
229
- name: 'Test User',
230
- email: 'test@example.com',
231
- });
232
- });
233
- });
234
-
235
- describe('helper hooks', () => {
236
- it('should work with useTypedQuery helper', async () => {
237
- const typedClient = createTypedQueryClient<paths>({
238
- baseURL: 'https://api.example.com',
239
- });
240
-
241
- const { result } = renderHook(
242
- () =>
243
- useTypedQuery(typedClient, 'GET /users/{id}', {
244
- params: { id: '789' },
245
- }),
246
- { wrapper },
247
- );
248
-
249
- await waitFor(() => {
250
- expect(result.current.isSuccess).toBe(true);
251
- });
252
-
253
- expect(result.current.data).toMatchObject({
254
- id: '789',
255
- name: 'John Doe',
256
- email: 'john@example.com',
257
- });
258
- });
259
-
260
- it('should work with useTypedMutation helper', async () => {
261
- const typedClient = createTypedQueryClient<paths>({
262
- baseURL: 'https://api.example.com',
263
- });
264
-
265
- const { result } = renderHook(
266
- () => useTypedMutation(typedClient, 'DELETE /users/{id}'),
267
- { wrapper },
268
- );
269
-
270
- act(() => {
271
- result.current.mutate({
272
- params: { id: '999' },
273
- });
274
- });
275
-
276
- await waitFor(() => {
277
- expect(result.current.isSuccess).toBe(true);
278
- });
279
-
280
- expect(result.current.data).toBeUndefined();
281
- });
282
- });
283
-
284
- describe('query key generation', () => {
285
- it('should generate correct query keys', async () => {
286
- const typedClient = createTypedQueryClient<paths>({
287
- baseURL: 'https://api.example.com',
288
- });
289
-
290
- // Test with path params only
291
- const { result: result1 } = renderHook(
292
- () =>
293
- typedClient.useQuery('GET /users/{id}', {
294
- params: { id: '123' },
295
- }),
296
- { wrapper },
297
- );
298
-
299
- await waitFor(() => expect(result1.current.isSuccess).toBe(true));
300
-
301
- // The query key should include the endpoint and params
302
- const queryState = queryClient.getQueryState([
303
- 'GET /users/{id}',
304
- { params: { id: '123' } },
305
- ]);
306
- expect(queryState).toBeDefined();
307
-
308
- // Test with query params
309
- const { result: result2 } = renderHook(
310
- () =>
311
- typedClient.useQuery('GET /posts', {
312
- query: { page: 1, limit: 10 },
313
- }),
314
- { wrapper },
315
- );
316
-
317
- await waitFor(() => expect(result2.current.isSuccess).toBe(true));
318
-
319
- const queryState2 = queryClient.getQueryState([
320
- 'GET /posts',
321
- { query: { page: 1, limit: 10 } },
322
- ]);
323
- expect(queryState2).toBeDefined();
324
- });
325
- });
326
-
327
- describe('error handling', () => {
328
- it('should handle 404 errors', async () => {
329
- const typedClient = createTypedQueryClient<paths>({
330
- baseURL: 'https://api.example.com',
331
- });
332
-
333
- const { result } = renderHook(
334
- () =>
335
- typedClient.useQuery('GET /users/{id}', {
336
- params: { id: '404' },
337
- }),
338
- { wrapper },
339
- );
340
-
341
- await waitFor(() => {
342
- expect(result.current.isError).toBe(true);
343
- });
344
-
345
- expect(result.current.error?.status).toBe(404);
346
- });
347
-
348
- it('should handle authorization errors', async () => {
349
- const typedClient = createTypedQueryClient<paths>({
350
- baseURL: 'https://api.example.com',
351
- });
352
-
353
- const { result } = renderHook(
354
- () => typedClient.useQuery('GET /protected'),
355
- { wrapper },
356
- );
357
-
358
- await waitFor(() => {
359
- expect(result.current.isError).toBe(true);
360
- });
361
-
362
- expect(result.current.error?.status).toBe(401);
363
- });
364
- });
365
-
366
- describe('Memoization', () => {
367
- it('should not cause unnecessary re-renders when options object is recreated with same values', async () => {
368
- const typedClient = createTypedQueryClient<paths>({
369
- baseURL: 'https://api.example.com',
370
- });
371
-
372
- // Track API calls using MSW request interception
373
- let requestCount = 0;
374
- const { server } = await import('./setup');
375
- server.use(
376
- http.get('https://api.example.com/users/:id', () => {
377
- requestCount++;
378
- return HttpResponse.json({ id: '123', name: 'John Doe' });
379
- }),
380
- );
381
-
382
- let renderCount = 0;
383
-
384
- const { rerender } = renderHook(
385
- ({ options }: { options?: { enabled?: boolean } }) => {
386
- renderCount++;
387
- return typedClient.useQuery(
388
- 'GET /users/{id}',
389
- { params: { id: '123' } },
390
- options,
391
- );
392
- },
393
- {
394
- wrapper,
395
- initialProps: { options: { enabled: true } },
396
- },
397
- );
398
-
399
- await waitFor(() => {
400
- expect(requestCount).toBe(1);
401
- });
402
-
403
- const initialRenderCount = renderCount;
404
-
405
- // Rerender with a new options object that has the same values
406
- rerender({ options: { enabled: true } });
407
-
408
- // Give React time to potentially trigger re-renders
409
- await act(async () => {
410
- await new Promise((resolve) => setTimeout(resolve, 10));
411
- });
412
-
413
- // The API should not be called again because options haven't actually changed
414
- expect(requestCount).toBe(1);
415
-
416
- // Component should re-render due to props change, but React Query shouldn't refetch
417
- expect(renderCount).toBeGreaterThan(initialRenderCount);
418
- });
419
-
420
- it('should re-fetch when options actually change', async () => {
421
- const typedClient = createTypedQueryClient<paths>({
422
- baseURL: 'https://api.example.com',
423
- });
424
-
425
- // Track API calls using MSW request interception
426
- let requestCount = 0;
427
- const { server } = await import('./setup');
428
- server.use(
429
- http.get('https://api.example.com/users/:id', () => {
430
- requestCount++;
431
- return HttpResponse.json({ id: '123', name: 'John Doe' });
432
- }),
433
- );
434
-
435
- const { rerender } = renderHook(
436
- ({ enabled }: { enabled: boolean }) =>
437
- typedClient.useQuery(
438
- 'GET /users/{id}',
439
- { params: { id: '123' } },
440
- { enabled },
441
- ),
442
- {
443
- wrapper,
444
- initialProps: { enabled: true },
445
- },
446
- );
447
-
448
- await waitFor(() => {
449
- expect(requestCount).toBe(1);
450
- });
451
-
452
- // Disable the query
453
- rerender({ enabled: false });
454
-
455
- await act(async () => {
456
- await new Promise((resolve) => setTimeout(resolve, 10));
457
- });
458
-
459
- // Should still only be called once since we disabled it
460
- expect(requestCount).toBe(1);
461
-
462
- // Re-enable the query
463
- rerender({ enabled: true });
464
-
465
- await waitFor(() => {
466
- expect(requestCount).toBe(2);
467
- });
468
- });
469
-
470
- it('should memoize mutation options correctly', async () => {
471
- const typedClient = createTypedQueryClient<paths>({
472
- baseURL: 'https://api.example.com',
473
- });
474
-
475
- // Track API calls using MSW request interception
476
- let requestCount = 0;
477
- const { server } = await import('./setup');
478
- server.use(
479
- http.post('https://api.example.com/users', async ({ request }) => {
480
- requestCount++;
481
- const body = await request.json();
482
- return HttpResponse.json({ id: '123', name: (body as any).name });
483
- }),
484
- );
485
-
486
- let renderCount = 0;
487
- let successCallCount = 0;
488
-
489
- const { result, rerender } = renderHook(
490
- ({ options }: { options?: { onSuccess?: () => void } }) => {
491
- renderCount++;
492
- return typedClient.useMutation('POST /users', options);
493
- },
494
- {
495
- wrapper,
496
- initialProps: {
497
- options: {
498
- onSuccess: () => {
499
- successCallCount++;
500
- },
501
- },
502
- },
503
- },
504
- );
505
-
506
- const initialRenderCount = renderCount;
507
-
508
- // Trigger a mutation to test the options
509
- await act(async () => {
510
- result.current.mutate({
511
- body: { name: 'Test User', email: 'test@example.com' },
512
- });
513
- });
514
-
515
- await waitFor(() => {
516
- expect(requestCount).toBe(1);
517
- expect(successCallCount).toBe(1);
518
- });
519
-
520
- // Rerender with a new options object that has the same function reference
521
- const onSuccess = () => {
522
- successCallCount++;
523
- };
524
- rerender({ options: { onSuccess } });
525
- rerender({ options: { onSuccess } });
526
-
527
- // Component should re-render due to props change
528
- expect(renderCount).toBeGreaterThan(initialRenderCount);
529
- });
530
-
531
- it('should handle config changes in useQuery correctly', async () => {
532
- const typedClient = createTypedQueryClient<paths>({
533
- baseURL: 'https://api.example.com',
534
- });
535
-
536
- // Track API calls using MSW request interception
537
- let requestCount = 0;
538
- const requestedIds: string[] = [];
539
- const { server } = await import('./setup');
540
- server.use(
541
- http.get('https://api.example.com/users/:id', ({ params }) => {
542
- requestCount++;
543
- const { id } = params;
544
- requestedIds.push(id as string);
545
- return HttpResponse.json({ id, name: `User ${id}` });
546
- }),
547
- );
548
-
549
- const { rerender } = renderHook(
550
- ({ userId }: { userId: string }) =>
551
- typedClient.useQuery('GET /users/{id}', { params: { id: userId } }),
552
- {
553
- wrapper,
554
- initialProps: { userId: '123' },
555
- },
556
- );
557
-
558
- await waitFor(() => {
559
- expect(requestCount).toBe(1);
560
- expect(requestedIds).toEqual(['123']);
561
- });
562
-
563
- // Change the user ID - should trigger a new fetch
564
- rerender({ userId: '456' });
565
-
566
- await waitFor(() => {
567
- expect(requestCount).toBe(2);
568
- expect(requestedIds).toEqual(['123', '456']);
569
- });
570
-
571
- // Call with same ID again - should not trigger new fetch due to React Query caching
572
- rerender({ userId: '456' });
573
-
574
- await act(async () => {
575
- await new Promise((resolve) => setTimeout(resolve, 10));
576
- });
577
-
578
- expect(requestCount).toBe(2);
579
- expect(requestedIds).toEqual(['123', '456']);
580
- });
581
- });
582
- });