@geekmidas/client 9.0.2 → 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 -146
  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,580 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createTypedFetcher, type FetchFn } from '../fetcher';
3
- import type { paths } from '../openapi-types';
4
- import './setup';
5
-
6
- describe('TypedFetcher', () => {
7
- it('should make GET request to fetch users', async () => {
8
- const client = createTypedFetcher<paths>({
9
- baseURL: 'https://api.example.com',
10
- });
11
-
12
- const result = await client('GET /users');
13
-
14
- expect(result).toEqual({
15
- users: [
16
- { id: '1', name: 'John Doe', email: 'john@example.com' },
17
- { id: '2', name: 'Jane Smith', email: 'jane@example.com' },
18
- ],
19
- });
20
- });
21
-
22
- it('should make GET request with path params', async () => {
23
- const client = createTypedFetcher<paths>({
24
- baseURL: 'https://api.example.com',
25
- });
26
-
27
- const result = await client('GET /users/{id}', { params: { id: '123' } });
28
-
29
- expect(result).toEqual({
30
- id: '123',
31
- name: 'John Doe',
32
- email: 'john@example.com',
33
- });
34
- });
35
-
36
- it('should make POST request with body', async () => {
37
- const client = createTypedFetcher<paths>({
38
- baseURL: 'https://api.example.com',
39
- });
40
-
41
- const requestBody = { name: 'Jane Doe', email: 'jane@example.com' };
42
- const result = await client('POST /users', { body: requestBody });
43
-
44
- expect(result).toEqual({
45
- id: '123',
46
- name: 'Jane Doe',
47
- email: 'jane@example.com',
48
- });
49
- });
50
-
51
- it('should make PUT request with path params and body', async () => {
52
- const client = createTypedFetcher<paths>({
53
- baseURL: 'https://api.example.com',
54
- });
55
-
56
- const result = await client('PUT /users/{id}', {
57
- params: { id: '456' },
58
- body: { name: 'Updated Name' },
59
- });
60
-
61
- expect(result).toEqual({
62
- id: '456',
63
- name: 'Updated Name',
64
- email: 'john@example.com',
65
- });
66
- });
67
-
68
- it('should handle DELETE requests', async () => {
69
- const client = createTypedFetcher<paths>({
70
- baseURL: 'https://api.example.com',
71
- });
72
-
73
- // DELETE returns no content (204)
74
- const result = await client('DELETE /users/{id}', {
75
- params: { id: '123' },
76
- });
77
-
78
- // For 204 responses, result should be undefined or empty
79
- expect(result).toBeUndefined();
80
- });
81
-
82
- it('should handle query parameters', async () => {
83
- const client = createTypedFetcher<paths>({
84
- baseURL: 'https://api.example.com',
85
- });
86
-
87
- const result = await client('GET /posts', {
88
- query: { page: 2, limit: 5, sort: 'desc' },
89
- });
90
-
91
- expect(result.posts).toHaveLength(5);
92
- });
93
-
94
- it('should handle array query parameters', async () => {
95
- // Mock fetch to capture the request URL
96
- const mockFetch = vi.fn().mockResolvedValue({
97
- ok: true,
98
- status: 200,
99
- headers: new Headers({ 'content-type': 'application/json' }),
100
- json: async () => ({ items: ['a', 'b', 'c'] }),
101
- });
102
-
103
- const client = createTypedFetcher<any>({
104
- baseURL: 'https://api.example.com',
105
- fetch: mockFetch,
106
- });
107
-
108
- await client('GET /search', {
109
- query: { tags: ['nodejs', 'typescript', 'javascript'] as any },
110
- });
111
-
112
- expect(mockFetch).toHaveBeenCalledWith(
113
- 'https://api.example.com/search?tags%5B%5D=nodejs&tags%5B%5D=typescript&tags%5B%5D=javascript',
114
- expect.any(Object),
115
- );
116
- });
117
-
118
- it('should handle object query parameters with bracket notation', async () => {
119
- // Mock fetch to capture the request URL
120
- const mockFetch = vi.fn().mockResolvedValue({
121
- ok: true,
122
- status: 200,
123
- headers: new Headers({ 'content-type': 'application/json' }),
124
- json: async () => ({ results: [] }),
125
- });
126
-
127
- const client = createTypedFetcher<any>({
128
- baseURL: 'https://api.example.com',
129
- fetch: mockFetch,
130
- });
131
-
132
- await client('GET /products', {
133
- query: {
134
- filter: {
135
- category: 'electronics',
136
- minPrice: 100,
137
- maxPrice: 500,
138
- },
139
- sort: 'price',
140
- } as any,
141
- });
142
-
143
- expect(mockFetch).toHaveBeenCalledWith(
144
- 'https://api.example.com/products?filter%5Bcategory%5D=electronics&filter%5BminPrice%5D=100&filter%5BmaxPrice%5D=500&sort=price',
145
- expect.any(Object),
146
- );
147
- });
148
-
149
- it('should handle arrays within nested objects', async () => {
150
- // Mock fetch to capture the request URL
151
- const mockFetch = vi.fn().mockResolvedValue({
152
- ok: true,
153
- status: 200,
154
- headers: new Headers({ 'content-type': 'application/json' }),
155
- json: async () => ({ results: [] }),
156
- });
157
-
158
- const client = createTypedFetcher<any>({
159
- baseURL: 'https://api.example.com',
160
- fetch: mockFetch,
161
- });
162
-
163
- await client('GET /advanced-search', {
164
- query: {
165
- user: {
166
- roles: ['admin', 'moderator', 'user'],
167
- status: 'active',
168
- },
169
- settings: {
170
- notifications: {
171
- types: ['email', 'sms', 'push'],
172
- enabled: true,
173
- },
174
- },
175
- } as any,
176
- });
177
-
178
- // qs uses bracket notation: user[roles][]=admin&user[status]=active etc.
179
- const calledUrl = mockFetch.mock.calls[0][0] as string;
180
- const url = new URL(calledUrl);
181
- expect(url.pathname).toBe('/advanced-search');
182
-
183
- // Verify the query string can be parsed back to the original object
184
- const qs = await import('qs');
185
- const parsed = qs.default.parse(url.search.slice(1));
186
- expect(parsed).toEqual({
187
- user: {
188
- roles: ['admin', 'moderator', 'user'],
189
- status: 'active',
190
- },
191
- settings: {
192
- notifications: {
193
- types: ['email', 'sms', 'push'],
194
- enabled: 'true',
195
- },
196
- },
197
- });
198
- });
199
-
200
- it('should handle 404 errors', async () => {
201
- const client = createTypedFetcher<paths>({
202
- baseURL: 'https://api.example.com',
203
- });
204
-
205
- try {
206
- await client('GET /users/{id}', {
207
- params: { id: '404' },
208
- });
209
- expect.fail('Should have thrown an error');
210
- } catch (error) {
211
- expect(error).toBeInstanceOf(Response);
212
- const response = error as Response;
213
- expect(response.status).toBe(404);
214
- const data = await response.json();
215
- expect(data.message).toBe('User not found');
216
- }
217
- });
218
-
219
- it('should handle 500 errors', async () => {
220
- const client = createTypedFetcher<paths>({
221
- baseURL: 'https://api.example.com',
222
- });
223
-
224
- try {
225
- await client('GET /error');
226
- expect.fail('Should have thrown an error');
227
- } catch (error) {
228
- expect(error).toBeInstanceOf(Response);
229
- const response = error as Response;
230
- expect(response.status).toBe(500);
231
- const data = await response.json();
232
- expect(data.message).toBe('Internal server error');
233
- }
234
- });
235
-
236
- it('should apply request interceptor', async () => {
237
- const onRequest = vi.fn((config) => ({
238
- ...config,
239
- headers: { ...config.headers, 'X-Custom-Header': 'test-value' },
240
- }));
241
-
242
- const client = createTypedFetcher<paths>({
243
- baseURL: 'https://api.example.com',
244
- onRequest,
245
- });
246
-
247
- await client('GET /users/{id}', { params: { id: '123' } });
248
-
249
- expect(onRequest).toHaveBeenCalledWith(
250
- expect.objectContaining({
251
- method: 'GET',
252
- headers: expect.any(Object),
253
- }),
254
- );
255
- });
256
-
257
- it('should apply response interceptor', async () => {
258
- const onResponse = vi.fn((response) => response);
259
-
260
- const client = createTypedFetcher<paths>({
261
- baseURL: 'https://api.example.com',
262
- onResponse,
263
- });
264
-
265
- await client('GET /users/{id}', { params: { id: '123' } });
266
-
267
- expect(onResponse).toHaveBeenCalledWith(expect.any(Response));
268
- });
269
-
270
- it('should apply error handler', async () => {
271
- const onError = vi.fn();
272
-
273
- const client = createTypedFetcher<paths>({
274
- baseURL: 'https://api.example.com',
275
- onError,
276
- });
277
-
278
- await expect(
279
- client('GET /users/{id}', { params: { id: '404' } }),
280
- ).rejects.toThrow();
281
-
282
- expect(onError).toHaveBeenCalledWith(expect.any(Response));
283
- });
284
-
285
- it('should merge default headers with request headers', async () => {
286
- const client = createTypedFetcher<paths>({
287
- baseURL: 'https://api.example.com',
288
- headers: { Authorization: 'Bearer valid-token' },
289
- });
290
-
291
- const result = await client('GET /protected', {
292
- headers: { 'X-Custom': 'value' },
293
- });
294
-
295
- expect(result).toEqual({ message: 'Protected data' });
296
- });
297
-
298
- it('should handle unauthorized requests', async () => {
299
- const client = createTypedFetcher<paths>({
300
- baseURL: 'https://api.example.com',
301
- headers: { Authorization: 'Bearer broken-token' },
302
- });
303
-
304
- try {
305
- await client('GET /protected');
306
- expect.fail('Should have thrown an error');
307
- } catch (error) {
308
- expect(error).toBeInstanceOf(Response);
309
- const response = error as Response;
310
- expect(response.status).toBe(401);
311
- const data = await response.json();
312
- expect(data.message).toBe('Unauthorized');
313
- }
314
- });
315
-
316
- it('should handle empty query parameters', async () => {
317
- const client = createTypedFetcher<paths>({
318
- baseURL: 'https://api.example.com',
319
- });
320
-
321
- const result = await client('GET /posts', {
322
- query: { page: 1, sort: undefined as any },
323
- });
324
-
325
- expect(result.pagination.page).toBe(1);
326
- expect(result.sort).toBe('asc'); // Default value when sort is undefined
327
- });
328
-
329
- it('should properly encode path parameters', async () => {
330
- const client = createTypedFetcher<paths>({
331
- baseURL: 'https://api.example.com',
332
- });
333
-
334
- const result = await client('GET /users/{id}', {
335
- params: { id: 'user with spaces' },
336
- });
337
-
338
- // MSW decodes the URL parameters, so we get the original value back
339
- expect(result).toEqual({
340
- id: 'user with spaces',
341
- name: 'John Doe',
342
- email: 'john@example.com',
343
- });
344
- });
345
-
346
- it('should correctly substitute multiple path parameters', async () => {
347
- // Add a mock handler for this test
348
- const mockFetch = vi.fn(async (url: string) => {
349
- if (url === 'https://api.example.com/posts/123/comments/456') {
350
- return new Response(
351
- JSON.stringify({
352
- postId: '123',
353
- commentId: '456',
354
- content: 'Test comment',
355
- }),
356
- { status: 200, headers: { 'Content-Type': 'application/json' } },
357
- );
358
- }
359
- return new Response('Not found', { status: 404 });
360
- }) as FetchFn;
361
-
362
- const client = createTypedFetcher<paths>({
363
- baseURL: 'https://api.example.com',
364
- fetch: mockFetch,
365
- });
366
-
367
- const result = await client(
368
- 'GET /posts/{postId}/comments/{commentId}' as any,
369
- {
370
- params: { postId: '123', commentId: '456' },
371
- },
372
- );
373
-
374
- expect(mockFetch).toHaveBeenCalledWith(
375
- 'https://api.example.com/posts/123/comments/456',
376
- expect.any(Object),
377
- );
378
- expect(result).toEqual({
379
- postId: '123',
380
- commentId: '456',
381
- content: 'Test comment',
382
- });
383
- });
384
-
385
- describe('wrap', () => {
386
- it('should return ok: true with data on success', async () => {
387
- const client = createTypedFetcher<paths>({
388
- baseURL: 'https://api.example.com',
389
- });
390
- const wrappedClient = client.wrap();
391
-
392
- const result = await wrappedClient('GET /users');
393
-
394
- expect(result.ok).toBe(true);
395
- expect('error' in result).toBe(false);
396
- if (result.ok) {
397
- expect(result.data).toEqual({
398
- users: [
399
- { id: '1', name: 'John Doe', email: 'john@example.com' },
400
- { id: '2', name: 'Jane Smith', email: 'jane@example.com' },
401
- ],
402
- });
403
- }
404
- });
405
-
406
- it('should return ok: false with error on failure', async () => {
407
- const client = createTypedFetcher<paths>({
408
- baseURL: 'https://api.example.com',
409
- });
410
- const wrappedClient = client.wrap();
411
-
412
- const result = await wrappedClient('GET /users/{id}', {
413
- params: { id: '404' },
414
- });
415
-
416
- expect(result.ok).toBe(false);
417
- expect('data' in result).toBe(false);
418
- if (!result.ok) {
419
- expect(result.error).toBeInstanceOf(Response);
420
- const response = result.error as Response;
421
- expect(response.status).toBe(404);
422
- }
423
- });
424
-
425
- it('should return ok: false on 500 errors', async () => {
426
- const client = createTypedFetcher<paths>({
427
- baseURL: 'https://api.example.com',
428
- });
429
- const wrappedClient = client.wrap();
430
-
431
- const result = await wrappedClient('GET /error');
432
-
433
- expect(result.ok).toBe(false);
434
- expect('data' in result).toBe(false);
435
- if (!result.ok) {
436
- expect(result.error).toBeInstanceOf(Response);
437
- const response = result.error as Response;
438
- expect(response.status).toBe(500);
439
- }
440
- });
441
-
442
- it('should work with path params and body', async () => {
443
- const client = createTypedFetcher<paths>({
444
- baseURL: 'https://api.example.com',
445
- });
446
- const wrappedClient = client.wrap();
447
-
448
- const result = await wrappedClient('PUT /users/{id}', {
449
- params: { id: '456' },
450
- body: { name: 'Updated Name' },
451
- });
452
-
453
- expect(result.ok).toBe(true);
454
- expect('error' in result).toBe(false);
455
- if (result.ok) {
456
- expect(result.data).toEqual({
457
- id: '456',
458
- name: 'Updated Name',
459
- email: 'john@example.com',
460
- });
461
- }
462
- });
463
-
464
- it('should still call onError handler', async () => {
465
- const onError = vi.fn();
466
- const client = createTypedFetcher<paths>({
467
- baseURL: 'https://api.example.com',
468
- onError,
469
- });
470
- const wrappedClient = client.wrap();
471
-
472
- const result = await wrappedClient('GET /users/{id}', {
473
- params: { id: '404' },
474
- });
475
-
476
- expect(result.ok).toBe(false);
477
- expect('data' in result).toBe(false);
478
- expect(onError).toHaveBeenCalledWith(expect.any(Response));
479
- });
480
-
481
- it('should transform error with callback', async () => {
482
- const client = createTypedFetcher<paths>({
483
- baseURL: 'https://api.example.com',
484
- });
485
- const wrappedClient = client.wrap(async (error) => {
486
- const response = error as Response;
487
- const body = await response.json();
488
- return { status: response.status, message: body.message };
489
- });
490
-
491
- const result = await wrappedClient('GET /users/{id}', {
492
- params: { id: '404' },
493
- });
494
-
495
- expect(result.ok).toBe(false);
496
- if (!result.ok) {
497
- expect(result.error).toEqual({
498
- status: 404,
499
- message: 'User not found',
500
- });
501
- }
502
- });
503
-
504
- it('should infer error type from transformer', async () => {
505
- const client = createTypedFetcher<paths>({
506
- baseURL: 'https://api.example.com',
507
- });
508
- const wrappedClient = client.wrap(async (error) => {
509
- const response = error as Response;
510
- return { status: response.status, message: 'something went wrong' };
511
- });
512
-
513
- const result = await wrappedClient('GET /error');
514
-
515
- expect(result.ok).toBe(false);
516
- if (!result.ok) {
517
- expect(result.error.status).toBe(500);
518
- expect(result.error.message).toBe('something went wrong');
519
- }
520
- });
521
-
522
- it('should preserve extra properties on wrapped function', async () => {
523
- const client = createTypedFetcher<paths>({
524
- baseURL: 'https://api.example.com',
525
- });
526
-
527
- // Simulate attaching hooks (like useQuery/useMutation)
528
- const mockUseQuery = vi.fn();
529
- const mockUseMutation = vi.fn();
530
- (client as any).useQuery = mockUseQuery;
531
- (client as any).useMutation = mockUseMutation;
532
-
533
- const wrappedClient = client.wrap();
534
-
535
- expect((wrappedClient as any).useQuery).toBe(mockUseQuery);
536
- expect((wrappedClient as any).useMutation).toBe(mockUseMutation);
537
- });
538
- });
539
-
540
- it('should URL encode special characters in path parameters', async () => {
541
- // Mock fetch to verify the actual URL being called
542
- const mockFetch = vi.fn(async (url: string) => {
543
- if (
544
- url ===
545
- 'https://api.example.com/users/user%20with%20spaces%2Fand%2Fslashes'
546
- ) {
547
- return new Response(
548
- JSON.stringify({
549
- id: 'user with spaces/and/slashes',
550
- name: 'Test User',
551
- email: 'test@example.com',
552
- }),
553
- { status: 200, headers: { 'Content-Type': 'application/json' } },
554
- );
555
- }
556
- return new Response('Not found', { status: 404 });
557
- }) as typeof fetch;
558
-
559
- const client = createTypedFetcher<paths>({
560
- baseURL: 'https://api.example.com',
561
- fetch: mockFetch,
562
- });
563
-
564
- const result = await client('GET /users/{id}', {
565
- params: { id: 'user with spaces/and/slashes' },
566
- });
567
-
568
- // Verify the URL was properly encoded
569
- expect(mockFetch).toHaveBeenCalledWith(
570
- 'https://api.example.com/users/user%20with%20spaces%2Fand%2Fslashes',
571
- expect.any(Object),
572
- );
573
-
574
- expect(result).toEqual({
575
- id: 'user with spaces/and/slashes',
576
- name: 'Test User',
577
- email: 'test@example.com',
578
- });
579
- });
580
- });