@geekmidas/client 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/auth-fetcher.cjs +0 -1
  3. package/dist/auth-fetcher.cjs.map +1 -1
  4. package/dist/auth-fetcher.d.cts +1 -1
  5. package/dist/auth-fetcher.d.cts.map +1 -1
  6. package/dist/auth-fetcher.d.mts +1 -1
  7. package/dist/auth-fetcher.d.mts.map +1 -1
  8. package/dist/auth-fetcher.mjs +0 -1
  9. package/dist/auth-fetcher.mjs.map +1 -1
  10. package/dist/endpoint-hooks.cjs +10 -5
  11. package/dist/endpoint-hooks.cjs.map +1 -1
  12. package/dist/endpoint-hooks.d.cts +2 -2
  13. package/dist/endpoint-hooks.d.cts.map +1 -1
  14. package/dist/endpoint-hooks.d.mts +2 -2
  15. package/dist/endpoint-hooks.d.mts.map +1 -1
  16. package/dist/endpoint-hooks.mjs +10 -5
  17. package/dist/endpoint-hooks.mjs.map +1 -1
  18. package/dist/fetcher-5fnBE7Dk.mjs.map +1 -1
  19. package/dist/fetcher-CgLEaIAC.cjs.map +1 -1
  20. package/dist/fetcher.d.cts +1 -1
  21. package/dist/fetcher.d.cts.map +1 -1
  22. package/dist/fetcher.d.mts +1 -1
  23. package/dist/fetcher.d.mts.map +1 -1
  24. package/dist/infer.d.cts.map +1 -1
  25. package/dist/infer.d.mts.map +1 -1
  26. package/dist/openapi-hooks.cjs.map +1 -1
  27. package/dist/openapi-hooks.d.cts +2 -2
  28. package/dist/openapi-hooks.d.cts.map +1 -1
  29. package/dist/openapi-hooks.d.mts +5 -5
  30. package/dist/openapi-hooks.d.mts.map +1 -1
  31. package/dist/openapi-hooks.mjs.map +1 -1
  32. package/dist/openapi-types.d.cts.map +1 -1
  33. package/dist/openapi-types.d.mts.map +1 -1
  34. package/dist/openapi.cjs.map +1 -1
  35. package/dist/openapi.mjs.map +1 -1
  36. package/dist/react-query.cjs.map +1 -1
  37. package/dist/react-query.d.cts +1 -1
  38. package/dist/react-query.d.cts.map +1 -1
  39. package/dist/react-query.d.mts +8 -8
  40. package/dist/react-query.d.mts.map +1 -1
  41. package/dist/react-query.mjs.map +1 -1
  42. package/dist/{types-FxummKaL.d.mts → types-4K4N-Fl1.d.cts} +2 -2
  43. package/dist/types-4K4N-Fl1.d.cts.map +1 -0
  44. package/dist/{types-vutATyWv.d.cts → types-hhkZWjQn.d.mts} +2 -2
  45. package/dist/types-hhkZWjQn.d.mts.map +1 -0
  46. package/dist/types.d.cts +1 -1
  47. package/dist/types.d.mts +1 -1
  48. package/package.json +5 -5
  49. package/src/__tests__/auth-fetcher.spec.ts +476 -476
  50. package/src/__tests__/endpoint-hooks.spec.tsx +348 -348
  51. package/src/__tests__/fetcher.spec.ts +403 -403
  52. package/src/__tests__/infer.integration.spec.ts +171 -171
  53. package/src/__tests__/infer.spec.ts +182 -182
  54. package/src/__tests__/method-restrictions.spec.tsx +165 -165
  55. package/src/__tests__/openapi-hooks.spec.tsx +536 -536
  56. package/src/__tests__/react-query-infinite.spec.tsx +989 -989
  57. package/src/__tests__/react-query-invalidation.spec.tsx +223 -222
  58. package/src/__tests__/react-query.spec.tsx +567 -567
  59. package/src/__tests__/setup.ts +260 -260
  60. package/src/__tests__/types.spec.ts +127 -127
  61. package/src/__tests__/url-parsing.spec.ts +191 -191
  62. package/src/auth-fetcher.ts +160 -162
  63. package/src/endpoint-hooks.ts +151 -156
  64. package/src/fetcher.ts +163 -163
  65. package/src/infer.ts +64 -63
  66. package/src/openapi-hooks.ts +146 -146
  67. package/src/openapi-types.d.ts +428 -428
  68. package/src/openapi.json +593 -593
  69. package/src/react-query.ts +281 -281
  70. package/src/types.ts +121 -124
  71. package/dist/types-FxummKaL.d.mts.map +0 -1
  72. package/dist/types-vutATyWv.d.cts.map +0 -1
@@ -8,231 +8,232 @@ import React from 'react';
8
8
  import { beforeEach, describe, expect, it, vi } from 'vitest';
9
9
  import type { paths } from '../openapi-types';
10
10
  import { TypedQueryClient, useTypedInvalidateQueries } from '../react-query';
11
+
11
12
  // Mock the fetcher module
12
13
  vi.mock('../fetcher', () => ({
13
- createTypedFetcher: () => vi.fn(),
14
+ createTypedFetcher: () => vi.fn(),
14
15
  }));
15
16
 
16
17
  describe('TypedQueryClient - Query Invalidation', () => {
17
- let queryClient: QueryClient;
18
- let typedClient: TypedQueryClient<paths>;
19
-
20
- beforeEach(() => {
21
- queryClient = new QueryClient({
22
- defaultOptions: {
23
- queries: { retry: false },
24
- },
25
- });
26
- typedClient = new TypedQueryClient({ queryClient });
27
- });
28
-
29
- describe('buildQueryKey', () => {
30
- it('should build query key with endpoint only', () => {
31
- const key = typedClient.buildQueryKey('GET /users');
32
- expect(key).toEqual(['GET /users']);
33
- });
34
-
35
- it('should build query key with params', () => {
36
- const key = typedClient.buildQueryKey('GET /users/{id}', {
37
- params: { id: '123' },
38
- });
39
- expect(key).toEqual(['GET /users/{id}', { params: { id: '123' } }]);
40
- });
41
-
42
- it('should build query key with query params', () => {
43
- const key = typedClient.buildQueryKey('GET /users', {
44
- query: { page: 1, limit: 10 },
45
- });
46
- expect(key).toEqual(['GET /users', { query: { page: 1, limit: 10 } }]);
47
- });
48
-
49
- it('should build query key with params only for paths without query support', () => {
50
- const key = typedClient.buildQueryKey('GET /users/{id}', {
51
- params: { id: '123' },
52
- });
53
- expect(key).toEqual(['GET /users/{id}', { params: { id: '123' } }]);
54
- });
55
- });
56
-
57
- describe('invalidateQueries', () => {
58
- it('should invalidate queries with endpoint only (partial match)', async () => {
59
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
60
-
61
- await typedClient.invalidateQueries('GET /users');
62
-
63
- expect(invalidateSpy).toHaveBeenCalledWith({
64
- queryKey: ['GET /users'],
65
- exact: false,
66
- });
67
- });
68
-
69
- it('should invalidate queries with config (exact match)', async () => {
70
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
71
-
72
- await typedClient.invalidateQueries('GET /users/{id}', {
73
- params: { id: '123' },
74
- });
75
-
76
- expect(invalidateSpy).toHaveBeenCalledWith({
77
- queryKey: ['GET /users/{id}', { params: { id: '123' } }],
78
- exact: true,
79
- });
80
- });
81
-
82
- it('should invalidate queries with query params', async () => {
83
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
84
-
85
- await typedClient.invalidateQueries('GET /posts', {
86
- query: { sort: 'asc' },
87
- });
88
-
89
- expect(invalidateSpy).toHaveBeenCalledWith({
90
- queryKey: ['GET /posts', { query: { sort: 'asc' } }],
91
- exact: true,
92
- });
93
- });
94
- });
95
-
96
- describe('invalidateAllQueries', () => {
97
- it('should invalidate all queries', async () => {
98
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
99
-
100
- await typedClient.invalidateAllQueries();
101
-
102
- expect(invalidateSpy).toHaveBeenCalledWith();
103
- });
104
- });
105
-
106
- describe('getQueryClient / setQueryClient', () => {
107
- it('should return the query client when provided in constructor', () => {
108
- const client = typedClient.getQueryClient();
109
- expect(client).toBe(queryClient);
110
- });
111
-
112
- it('should throw error when no query client is set', () => {
113
- const clientWithoutQC = new TypedQueryClient();
114
-
115
- expect(() => clientWithoutQC.getQueryClient()).toThrow(
116
- 'No QueryClient set, please provide a QueryClient via the queryClient option or ensure you are within a QueryClientProvider',
117
- );
118
- });
119
-
120
- it('should allow setting query client after construction', () => {
121
- const clientWithoutQC = new TypedQueryClient();
122
- const newQueryClient = new QueryClient();
123
-
124
- clientWithoutQC.setQueryClient(newQueryClient);
125
-
126
- expect(clientWithoutQC.getQueryClient()).toBe(newQueryClient);
127
- });
128
- });
129
-
130
- describe('useTypedInvalidateQueries hook', () => {
131
- const wrapper = ({ children }: { children: React.ReactNode }) => (
132
- <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
133
- );
134
-
135
- it('should provide invalidateQueries function', async () => {
136
- const { result } = renderHook(
137
- () => useTypedInvalidateQueries(typedClient),
138
- { wrapper },
139
- );
140
-
141
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
142
-
143
- await result.current.invalidateQueries('GET /users/{id}', {
144
- params: { id: '456' },
145
- });
146
-
147
- expect(invalidateSpy).toHaveBeenCalledWith({
148
- queryKey: ['GET /users/{id}', { params: { id: '456' } }],
149
- exact: true,
150
- });
151
- });
152
-
153
- it('should provide invalidateAllQueries function', async () => {
154
- const { result } = renderHook(
155
- () => useTypedInvalidateQueries(typedClient),
156
- { wrapper },
157
- );
158
-
159
- const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
160
-
161
- await result.current.invalidateAllQueries();
162
-
163
- expect(invalidateSpy).toHaveBeenCalledWith();
164
- });
165
- });
166
-
167
- describe('Integration test: Query invalidation with real queries', () => {
168
- it('should invalidate matching queries after mutation', async () => {
169
- // Set up some queries in the cache
170
- queryClient.setQueryData(
171
- ['GET /users'],
172
- [
173
- { id: '1', name: 'User 1' },
174
- { id: '2', name: 'User 2' },
175
- ],
176
- );
177
- queryClient.setQueryData(['GET /users/{id}', { params: { id: '1' } }], {
178
- id: '1',
179
- name: 'User 1',
180
- });
181
- queryClient.setQueryData(
182
- ['GET /users', { query: { page: 1 } }],
183
- [{ id: '1', name: 'User 1' }],
184
- );
185
-
186
- // Verify data is in cache
187
- expect(queryClient.getQueryData(['GET /users'])).toBeDefined();
188
- expect(
189
- queryClient.getQueryData(['GET /users/{id}', { params: { id: '1' } }]),
190
- ).toBeDefined();
191
- expect(
192
- queryClient.getQueryData(['GET /users', { query: { page: 1 } }]),
193
- ).toBeDefined();
194
-
195
- // Invalidate all user queries (partial match)
196
- await typedClient.invalidateQueries('GET /users');
197
-
198
- // All queries starting with 'GET /users' should be invalidated
199
- await waitFor(() => {
200
- const cache = queryClient.getQueryCache();
201
- const queries = cache.findAll({ queryKey: ['GET /users'] });
202
- queries.forEach((query) => {
203
- expect(query.state.isInvalidated).toBe(true);
204
- });
205
- });
206
- });
207
-
208
- it('should only invalidate exact matches when config is provided', async () => {
209
- // Set up queries
210
- queryClient.setQueryData(
211
- ['GET /users', { query: { page: 1 } }],
212
- [{ id: '1', name: 'User 1' }],
213
- );
214
- queryClient.setQueryData(
215
- ['GET /users', { query: { page: 2 } }],
216
- [{ id: '2', name: 'User 2' }],
217
- );
218
-
219
- // Invalidate only page 1
220
- await typedClient.invalidateQueries('GET /users', {
221
- query: { page: 1 },
222
- });
223
-
224
- await waitFor(() => {
225
- const cache = queryClient.getQueryCache();
226
- const page1Query = cache.find({
227
- queryKey: ['GET /users', { query: { page: 1 } }],
228
- });
229
- const page2Query = cache.find({
230
- queryKey: ['GET /users', { query: { page: 2 } }],
231
- });
232
-
233
- expect(page1Query?.state.isInvalidated).toBe(true);
234
- expect(page2Query?.state.isInvalidated).toBe(false);
235
- });
236
- });
237
- });
18
+ let queryClient: QueryClient;
19
+ let typedClient: TypedQueryClient<paths>;
20
+
21
+ beforeEach(() => {
22
+ queryClient = new QueryClient({
23
+ defaultOptions: {
24
+ queries: { retry: false },
25
+ },
26
+ });
27
+ typedClient = new TypedQueryClient({ queryClient });
28
+ });
29
+
30
+ describe('buildQueryKey', () => {
31
+ it('should build query key with endpoint only', () => {
32
+ const key = typedClient.buildQueryKey('GET /users');
33
+ expect(key).toEqual(['GET /users']);
34
+ });
35
+
36
+ it('should build query key with params', () => {
37
+ const key = typedClient.buildQueryKey('GET /users/{id}', {
38
+ params: { id: '123' },
39
+ });
40
+ expect(key).toEqual(['GET /users/{id}', { params: { id: '123' } }]);
41
+ });
42
+
43
+ it('should build query key with query params', () => {
44
+ const key = typedClient.buildQueryKey('GET /users', {
45
+ query: { page: 1, limit: 10 },
46
+ });
47
+ expect(key).toEqual(['GET /users', { query: { page: 1, limit: 10 } }]);
48
+ });
49
+
50
+ it('should build query key with params only for paths without query support', () => {
51
+ const key = typedClient.buildQueryKey('GET /users/{id}', {
52
+ params: { id: '123' },
53
+ });
54
+ expect(key).toEqual(['GET /users/{id}', { params: { id: '123' } }]);
55
+ });
56
+ });
57
+
58
+ describe('invalidateQueries', () => {
59
+ it('should invalidate queries with endpoint only (partial match)', async () => {
60
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
61
+
62
+ await typedClient.invalidateQueries('GET /users');
63
+
64
+ expect(invalidateSpy).toHaveBeenCalledWith({
65
+ queryKey: ['GET /users'],
66
+ exact: false,
67
+ });
68
+ });
69
+
70
+ it('should invalidate queries with config (exact match)', async () => {
71
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
72
+
73
+ await typedClient.invalidateQueries('GET /users/{id}', {
74
+ params: { id: '123' },
75
+ });
76
+
77
+ expect(invalidateSpy).toHaveBeenCalledWith({
78
+ queryKey: ['GET /users/{id}', { params: { id: '123' } }],
79
+ exact: true,
80
+ });
81
+ });
82
+
83
+ it('should invalidate queries with query params', async () => {
84
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
85
+
86
+ await typedClient.invalidateQueries('GET /posts', {
87
+ query: { sort: 'asc' },
88
+ });
89
+
90
+ expect(invalidateSpy).toHaveBeenCalledWith({
91
+ queryKey: ['GET /posts', { query: { sort: 'asc' } }],
92
+ exact: true,
93
+ });
94
+ });
95
+ });
96
+
97
+ describe('invalidateAllQueries', () => {
98
+ it('should invalidate all queries', async () => {
99
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
100
+
101
+ await typedClient.invalidateAllQueries();
102
+
103
+ expect(invalidateSpy).toHaveBeenCalledWith();
104
+ });
105
+ });
106
+
107
+ describe('getQueryClient / setQueryClient', () => {
108
+ it('should return the query client when provided in constructor', () => {
109
+ const client = typedClient.getQueryClient();
110
+ expect(client).toBe(queryClient);
111
+ });
112
+
113
+ it('should throw error when no query client is set', () => {
114
+ const clientWithoutQC = new TypedQueryClient();
115
+
116
+ expect(() => clientWithoutQC.getQueryClient()).toThrow(
117
+ 'No QueryClient set, please provide a QueryClient via the queryClient option or ensure you are within a QueryClientProvider',
118
+ );
119
+ });
120
+
121
+ it('should allow setting query client after construction', () => {
122
+ const clientWithoutQC = new TypedQueryClient();
123
+ const newQueryClient = new QueryClient();
124
+
125
+ clientWithoutQC.setQueryClient(newQueryClient);
126
+
127
+ expect(clientWithoutQC.getQueryClient()).toBe(newQueryClient);
128
+ });
129
+ });
130
+
131
+ describe('useTypedInvalidateQueries hook', () => {
132
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
133
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
134
+ );
135
+
136
+ it('should provide invalidateQueries function', async () => {
137
+ const { result } = renderHook(
138
+ () => useTypedInvalidateQueries(typedClient),
139
+ { wrapper },
140
+ );
141
+
142
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
143
+
144
+ await result.current.invalidateQueries('GET /users/{id}', {
145
+ params: { id: '456' },
146
+ });
147
+
148
+ expect(invalidateSpy).toHaveBeenCalledWith({
149
+ queryKey: ['GET /users/{id}', { params: { id: '456' } }],
150
+ exact: true,
151
+ });
152
+ });
153
+
154
+ it('should provide invalidateAllQueries function', async () => {
155
+ const { result } = renderHook(
156
+ () => useTypedInvalidateQueries(typedClient),
157
+ { wrapper },
158
+ );
159
+
160
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
161
+
162
+ await result.current.invalidateAllQueries();
163
+
164
+ expect(invalidateSpy).toHaveBeenCalledWith();
165
+ });
166
+ });
167
+
168
+ describe('Integration test: Query invalidation with real queries', () => {
169
+ it('should invalidate matching queries after mutation', async () => {
170
+ // Set up some queries in the cache
171
+ queryClient.setQueryData(
172
+ ['GET /users'],
173
+ [
174
+ { id: '1', name: 'User 1' },
175
+ { id: '2', name: 'User 2' },
176
+ ],
177
+ );
178
+ queryClient.setQueryData(['GET /users/{id}', { params: { id: '1' } }], {
179
+ id: '1',
180
+ name: 'User 1',
181
+ });
182
+ queryClient.setQueryData(
183
+ ['GET /users', { query: { page: 1 } }],
184
+ [{ id: '1', name: 'User 1' }],
185
+ );
186
+
187
+ // Verify data is in cache
188
+ expect(queryClient.getQueryData(['GET /users'])).toBeDefined();
189
+ expect(
190
+ queryClient.getQueryData(['GET /users/{id}', { params: { id: '1' } }]),
191
+ ).toBeDefined();
192
+ expect(
193
+ queryClient.getQueryData(['GET /users', { query: { page: 1 } }]),
194
+ ).toBeDefined();
195
+
196
+ // Invalidate all user queries (partial match)
197
+ await typedClient.invalidateQueries('GET /users');
198
+
199
+ // All queries starting with 'GET /users' should be invalidated
200
+ await waitFor(() => {
201
+ const cache = queryClient.getQueryCache();
202
+ const queries = cache.findAll({ queryKey: ['GET /users'] });
203
+ queries.forEach((query) => {
204
+ expect(query.state.isInvalidated).toBe(true);
205
+ });
206
+ });
207
+ });
208
+
209
+ it('should only invalidate exact matches when config is provided', async () => {
210
+ // Set up queries
211
+ queryClient.setQueryData(
212
+ ['GET /users', { query: { page: 1 } }],
213
+ [{ id: '1', name: 'User 1' }],
214
+ );
215
+ queryClient.setQueryData(
216
+ ['GET /users', { query: { page: 2 } }],
217
+ [{ id: '2', name: 'User 2' }],
218
+ );
219
+
220
+ // Invalidate only page 1
221
+ await typedClient.invalidateQueries('GET /users', {
222
+ query: { page: 1 },
223
+ });
224
+
225
+ await waitFor(() => {
226
+ const cache = queryClient.getQueryCache();
227
+ const page1Query = cache.find({
228
+ queryKey: ['GET /users', { query: { page: 1 } }],
229
+ });
230
+ const page2Query = cache.find({
231
+ queryKey: ['GET /users', { query: { page: 2 } }],
232
+ });
233
+
234
+ expect(page1Query?.state.isInvalidated).toBe(true);
235
+ expect(page2Query?.state.isInvalidated).toBe(false);
236
+ });
237
+ });
238
+ });
238
239
  });