@geekmidas/client 0.4.0 → 0.5.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.
- package/dist/auth-fetcher.cjs +0 -1
- package/dist/auth-fetcher.cjs.map +1 -1
- package/dist/auth-fetcher.d.cts +1 -1
- package/dist/auth-fetcher.d.cts.map +1 -1
- package/dist/auth-fetcher.d.mts +1 -1
- package/dist/auth-fetcher.d.mts.map +1 -1
- package/dist/auth-fetcher.mjs +0 -1
- package/dist/auth-fetcher.mjs.map +1 -1
- package/dist/endpoint-hooks.cjs +10 -5
- package/dist/endpoint-hooks.cjs.map +1 -1
- package/dist/endpoint-hooks.d.cts +2 -2
- package/dist/endpoint-hooks.d.cts.map +1 -1
- package/dist/endpoint-hooks.d.mts +2 -2
- package/dist/endpoint-hooks.d.mts.map +1 -1
- package/dist/endpoint-hooks.mjs +10 -5
- package/dist/endpoint-hooks.mjs.map +1 -1
- package/dist/fetcher-5fnBE7Dk.mjs.map +1 -1
- package/dist/fetcher-CgLEaIAC.cjs.map +1 -1
- package/dist/fetcher.d.cts +1 -1
- package/dist/fetcher.d.cts.map +1 -1
- package/dist/fetcher.d.mts +1 -1
- package/dist/fetcher.d.mts.map +1 -1
- package/dist/infer.d.cts.map +1 -1
- package/dist/infer.d.mts.map +1 -1
- package/dist/openapi-hooks.cjs.map +1 -1
- package/dist/openapi-hooks.d.cts +2 -2
- package/dist/openapi-hooks.d.cts.map +1 -1
- package/dist/openapi-hooks.d.mts +2 -2
- package/dist/openapi-hooks.d.mts.map +1 -1
- package/dist/openapi-hooks.mjs.map +1 -1
- package/dist/openapi-types.d.cts.map +1 -1
- package/dist/openapi-types.d.mts.map +1 -1
- package/dist/openapi.cjs.map +1 -1
- package/dist/openapi.mjs.map +1 -1
- package/dist/react-query.cjs.map +1 -1
- package/dist/react-query.d.cts +1 -1
- package/dist/react-query.d.cts.map +1 -1
- package/dist/react-query.d.mts +1 -1
- package/dist/react-query.d.mts.map +1 -1
- package/dist/react-query.mjs.map +1 -1
- package/dist/{types-FxummKaL.d.mts → types-4K4N-Fl1.d.cts} +2 -2
- package/dist/types-4K4N-Fl1.d.cts.map +1 -0
- package/dist/{types-vutATyWv.d.cts → types-hhkZWjQn.d.mts} +2 -2
- package/dist/types-hhkZWjQn.d.mts.map +1 -0
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +3 -3
- package/src/__tests__/auth-fetcher.spec.ts +476 -476
- package/src/__tests__/endpoint-hooks.spec.tsx +348 -348
- package/src/__tests__/fetcher.spec.ts +403 -403
- package/src/__tests__/infer.integration.spec.ts +171 -171
- package/src/__tests__/infer.spec.ts +182 -182
- package/src/__tests__/method-restrictions.spec.tsx +165 -165
- package/src/__tests__/openapi-hooks.spec.tsx +536 -536
- package/src/__tests__/react-query-infinite.spec.tsx +989 -989
- package/src/__tests__/react-query-invalidation.spec.tsx +223 -222
- package/src/__tests__/react-query.spec.tsx +567 -567
- package/src/__tests__/setup.ts +260 -260
- package/src/__tests__/types.spec.ts +127 -127
- package/src/__tests__/url-parsing.spec.ts +191 -191
- package/src/auth-fetcher.ts +160 -162
- package/src/endpoint-hooks.ts +151 -156
- package/src/fetcher.ts +163 -163
- package/src/infer.ts +64 -63
- package/src/openapi-hooks.ts +146 -146
- package/src/openapi-types.d.ts +428 -428
- package/src/openapi.json +593 -593
- package/src/react-query.ts +281 -281
- package/src/types.ts +121 -124
- package/dist/types-FxummKaL.d.mts.map +0 -1
- package/dist/types-vutATyWv.d.cts.map +0 -1
|
@@ -1,134 +1,134 @@
|
|
|
1
1
|
import { describe, expectTypeOf, it } from 'vitest';
|
|
2
2
|
import type { paths } from '../openapi-types';
|
|
3
3
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
ExtractEndpointConfig,
|
|
5
|
+
ExtractEndpointResponse,
|
|
6
|
+
FilteredRequestConfig,
|
|
7
|
+
TypedEndpoint,
|
|
8
|
+
ValidEndpoint,
|
|
9
9
|
} from '../types';
|
|
10
10
|
|
|
11
11
|
describe('Type utilities', () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
12
|
+
it('should extract valid endpoints from paths', () => {
|
|
13
|
+
type ValidEndpoints = TypedEndpoint<paths>;
|
|
14
|
+
|
|
15
|
+
// Test that valid endpoints are correctly generated
|
|
16
|
+
expectTypeOf<ValidEndpoints>().toExtend<
|
|
17
|
+
| 'GET /users'
|
|
18
|
+
| 'POST /users'
|
|
19
|
+
| 'GET /users/{id}'
|
|
20
|
+
| 'PUT /users/{id}'
|
|
21
|
+
| 'DELETE /users/{id}'
|
|
22
|
+
| 'GET /posts'
|
|
23
|
+
| 'GET /protected'
|
|
24
|
+
| 'GET /error'
|
|
25
|
+
>();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should create typed endpoint strings', () => {
|
|
29
|
+
type Endpoints = TypedEndpoint<paths>;
|
|
30
|
+
|
|
31
|
+
// Test that TypedEndpoint resolves to the same as ValidEndpoint
|
|
32
|
+
expectTypeOf<Endpoints>().toEqualTypeOf<ValidEndpoint<paths>>();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should extract response types correctly', () => {
|
|
36
|
+
type UserResponse = ExtractEndpointResponse<paths, 'GET /users/{id}'>;
|
|
37
|
+
type UsersResponse = ExtractEndpointResponse<paths, 'GET /users'>;
|
|
38
|
+
type PostsResponse = ExtractEndpointResponse<paths, 'GET /posts'>;
|
|
39
|
+
|
|
40
|
+
expectTypeOf<UserResponse>().toEqualTypeOf<{
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
email: string;
|
|
44
|
+
}>();
|
|
45
|
+
|
|
46
|
+
expectTypeOf<UsersResponse>().toEqualTypeOf<{
|
|
47
|
+
users: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
email: string;
|
|
51
|
+
}>;
|
|
52
|
+
}>();
|
|
53
|
+
|
|
54
|
+
expectTypeOf<PostsResponse>().toEqualTypeOf<{
|
|
55
|
+
posts: Array<{
|
|
56
|
+
id: string;
|
|
57
|
+
title: string;
|
|
58
|
+
content: string;
|
|
59
|
+
authorId: string;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
}>;
|
|
62
|
+
pagination: {
|
|
63
|
+
page: number;
|
|
64
|
+
limit: number;
|
|
65
|
+
total: number;
|
|
66
|
+
};
|
|
67
|
+
sort: 'asc' | 'desc';
|
|
68
|
+
}>();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should extract config types correctly', () => {
|
|
72
|
+
type GetUserConfig = ExtractEndpointConfig<paths, 'GET /users/{id}'>;
|
|
73
|
+
type PostUserConfig = ExtractEndpointConfig<paths, 'POST /users'>;
|
|
74
|
+
type GetPostsConfig = ExtractEndpointConfig<paths, 'GET /posts'>;
|
|
75
|
+
|
|
76
|
+
expectTypeOf<GetUserConfig>().toEqualTypeOf<{
|
|
77
|
+
params?: { id: string };
|
|
78
|
+
query?: never;
|
|
79
|
+
body?: never;
|
|
80
|
+
headers?: Record<string, string>;
|
|
81
|
+
}>();
|
|
82
|
+
|
|
83
|
+
expectTypeOf<PostUserConfig>().toEqualTypeOf<{
|
|
84
|
+
params?: never;
|
|
85
|
+
query?: never;
|
|
86
|
+
body?: { name: string; email: string };
|
|
87
|
+
headers?: Record<string, string>;
|
|
88
|
+
}>();
|
|
89
|
+
|
|
90
|
+
expectTypeOf<GetPostsConfig>().toEqualTypeOf<{
|
|
91
|
+
params?: never;
|
|
92
|
+
query?: {
|
|
93
|
+
page?: number;
|
|
94
|
+
limit?: number;
|
|
95
|
+
sort?: 'asc' | 'desc';
|
|
96
|
+
};
|
|
97
|
+
body?: never;
|
|
98
|
+
headers?: Record<string, string>;
|
|
99
|
+
}>();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should filter config to only required properties', () => {
|
|
103
|
+
type GetUserFiltered = FilteredRequestConfig<paths, 'GET /users/{id}'>;
|
|
104
|
+
type PostUserFiltered = FilteredRequestConfig<paths, 'POST /users'>;
|
|
105
|
+
type GetUsersFiltered = FilteredRequestConfig<paths, 'GET /users'>;
|
|
106
|
+
|
|
107
|
+
// GET /users/{id} should require params
|
|
108
|
+
expectTypeOf<GetUserFiltered>().toEqualTypeOf<{
|
|
109
|
+
params?: { id: string };
|
|
110
|
+
headers?: Record<string, string>;
|
|
111
|
+
}>();
|
|
112
|
+
|
|
113
|
+
// POST /users should allow body
|
|
114
|
+
expectTypeOf<PostUserFiltered>().toEqualTypeOf<{
|
|
115
|
+
body?: { name: string; email: string };
|
|
116
|
+
headers?: Record<string, string>;
|
|
117
|
+
}>();
|
|
118
|
+
|
|
119
|
+
// GET /users should only allow headers (no params, query, or body)
|
|
120
|
+
expectTypeOf<GetUsersFiltered>().toEqualTypeOf<{
|
|
121
|
+
headers?: Record<string, string>;
|
|
122
|
+
}>();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle endpoints with multiple parameter types', () => {
|
|
126
|
+
type PutUserConfig = FilteredRequestConfig<paths, 'PUT /users/{id}'>;
|
|
127
|
+
|
|
128
|
+
expectTypeOf<PutUserConfig>().toEqualTypeOf<{
|
|
129
|
+
params?: { id: string };
|
|
130
|
+
body?: { name?: string; email?: string };
|
|
131
|
+
headers?: Record<string, string>;
|
|
132
|
+
}>();
|
|
133
|
+
});
|
|
134
134
|
});
|
|
@@ -2,195 +2,195 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import { createTypedFetcher } from '../fetcher';
|
|
3
3
|
|
|
4
4
|
describe('URL Path Parameter Parsing', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
5
|
+
it('should correctly parse single path parameter', async () => {
|
|
6
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
7
|
+
new Response(JSON.stringify({ success: true }), {
|
|
8
|
+
status: 200,
|
|
9
|
+
headers: { 'Content-Type': 'application/json' },
|
|
10
|
+
}),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const client = createTypedFetcher<any>({
|
|
14
|
+
baseURL: 'https://api.example.com',
|
|
15
|
+
fetch: mockFetch,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
await client('GET /users/{userId}' as any, {
|
|
19
|
+
params: { userId: '12345' },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
23
|
+
'https://api.example.com/users/12345',
|
|
24
|
+
expect.any(Object),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should correctly parse multiple path parameters', async () => {
|
|
29
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
30
|
+
new Response(JSON.stringify({ success: true }), {
|
|
31
|
+
status: 200,
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const client = createTypedFetcher<any>({
|
|
37
|
+
fetch: mockFetch,
|
|
38
|
+
baseURL: 'https://api.example.com',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await client(
|
|
42
|
+
'GET /organizations/{orgId}/projects/{projectId}/tasks/{taskId}' as any,
|
|
43
|
+
{
|
|
44
|
+
params: {
|
|
45
|
+
orgId: 'org-123',
|
|
46
|
+
projectId: 'proj-456',
|
|
47
|
+
taskId: 'task-789',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
53
|
+
'https://api.example.com/organizations/org-123/projects/proj-456/tasks/task-789',
|
|
54
|
+
expect.any(Object),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should URL encode special characters in path parameters', async () => {
|
|
59
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
60
|
+
new Response(JSON.stringify({ success: true }), {
|
|
61
|
+
status: 200,
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const client = createTypedFetcher<any>({
|
|
67
|
+
baseURL: 'https://api.example.com',
|
|
68
|
+
fetch: mockFetch,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await client('GET /files/{filename}' as any, {
|
|
72
|
+
params: {
|
|
73
|
+
filename: 'my file with spaces & special#characters.txt',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
78
|
+
'https://api.example.com/files/my%20file%20with%20spaces%20%26%20special%23characters.txt',
|
|
79
|
+
expect.any(Object),
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should handle path parameters with forward slashes', async () => {
|
|
84
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
85
|
+
new Response(JSON.stringify({ success: true }), {
|
|
86
|
+
status: 200,
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const client = createTypedFetcher<any>({
|
|
92
|
+
baseURL: 'https://api.example.com',
|
|
93
|
+
fetch: mockFetch,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await client('GET /repositories/{repoPath}' as any, {
|
|
97
|
+
params: {
|
|
98
|
+
repoPath: 'owner/repo/branch',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
103
|
+
'https://api.example.com/repositories/owner%2Frepo%2Fbranch',
|
|
104
|
+
expect.any(Object),
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should handle path parameters and query parameters together', async () => {
|
|
109
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
110
|
+
new Response(JSON.stringify({ success: true }), {
|
|
111
|
+
status: 200,
|
|
112
|
+
headers: { 'Content-Type': 'application/json' },
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
const client = createTypedFetcher<any>({
|
|
116
|
+
baseURL: 'https://api.example.com',
|
|
117
|
+
fetch: mockFetch,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
await client('GET /users/{userId}/posts' as any, {
|
|
121
|
+
params: { userId: 'user-123' },
|
|
122
|
+
query: { page: 2, limit: 10, sort: 'desc' },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
126
|
+
'https://api.example.com/users/user-123/posts?page=2&limit=10&sort=desc',
|
|
127
|
+
expect.any(Object),
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle numeric path parameters', async () => {
|
|
132
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
133
|
+
new Response(JSON.stringify({ success: true }), {
|
|
134
|
+
status: 200,
|
|
135
|
+
headers: { 'Content-Type': 'application/json' },
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
const client = createTypedFetcher<any>({
|
|
139
|
+
baseURL: 'https://api.example.com',
|
|
140
|
+
fetch: mockFetch,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await client('GET /api/v{version}/users/{id}' as any, {
|
|
144
|
+
params: { version: 2, id: 12345 },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
148
|
+
'https://api.example.com/api/v2/users/12345',
|
|
149
|
+
expect.any(Object),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should handle empty baseURL', async () => {
|
|
154
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
155
|
+
new Response(JSON.stringify({ success: true }), {
|
|
156
|
+
status: 200,
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
const client = createTypedFetcher<any>({
|
|
161
|
+
baseURL: '',
|
|
162
|
+
fetch: mockFetch,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
await client('GET /users/{id}' as any, {
|
|
166
|
+
params: { id: '123' },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(mockFetch).toHaveBeenCalledWith('/users/123', expect.any(Object));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should not replace parameters that are not provided', async () => {
|
|
173
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
174
|
+
new Response(JSON.stringify({ success: true }), {
|
|
175
|
+
status: 200,
|
|
176
|
+
headers: { 'Content-Type': 'application/json' },
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const client = createTypedFetcher<any>({
|
|
181
|
+
baseURL: 'https://api.example.com',
|
|
182
|
+
fetch: mockFetch,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Missing the userId parameter
|
|
186
|
+
await client('GET /users/{userId}/posts/{postId}' as any, {
|
|
187
|
+
params: { postId: '456' },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// The {userId} should remain in the URL unchanged
|
|
191
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
192
|
+
'https://api.example.com/users/{userId}/posts/456',
|
|
193
|
+
expect.any(Object),
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
196
|
});
|