@geekmidas/client 0.0.1 → 0.1.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/README.md +153 -2
- package/dist/auth-fetcher.cjs +78 -0
- package/dist/auth-fetcher.cjs.map +1 -0
- package/dist/auth-fetcher.d.cts +157 -0
- package/dist/auth-fetcher.d.mts +157 -0
- package/dist/auth-fetcher.mjs +78 -0
- package/dist/auth-fetcher.mjs.map +1 -0
- package/dist/endpoint-hooks.cjs +60 -0
- package/dist/endpoint-hooks.cjs.map +1 -0
- package/dist/endpoint-hooks.d.cts +53 -0
- package/dist/endpoint-hooks.d.mts +53 -0
- package/dist/endpoint-hooks.mjs +59 -0
- package/dist/endpoint-hooks.mjs.map +1 -0
- package/dist/fetcher.d.cts +1 -1
- package/dist/fetcher.d.mts +1 -1
- package/dist/infer.cjs +0 -0
- package/dist/infer.d.cts +133 -0
- package/dist/infer.d.mts +133 -0
- package/dist/infer.mjs +0 -0
- package/dist/openapi-hooks.d.cts +4 -4
- package/dist/openapi-hooks.d.mts +1 -1
- package/dist/react-query.d.cts +8 -8
- package/dist/react-query.d.mts +1 -1
- package/dist/{types-csACmD6U.d.cts → types-CyMkwV3g.d.mts} +34 -3
- package/dist/{types-tMp85Lt_.d.mts → types-DVVUpwI4.d.cts} +34 -3
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +38 -3
- package/src/__tests__/endpoint-hooks.spec.tsx +374 -0
- package/src/__tests__/infer.integration.spec.ts +188 -0
- package/src/__tests__/infer.spec.ts +189 -0
- package/src/auth-fetcher.ts +288 -0
- package/src/endpoint-hooks.ts +203 -0
- package/src/infer.ts +161 -0
- package/src/types.ts +69 -8
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
QueryClient,
|
|
3
|
+
UseMutationOptions,
|
|
4
|
+
UseQueryOptions,
|
|
5
|
+
} from '@tanstack/react-query';
|
|
6
|
+
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
7
|
+
import { useMemo } from 'react';
|
|
8
|
+
import type {
|
|
9
|
+
ExtractEndpointResponse,
|
|
10
|
+
FilteredRequestConfig,
|
|
11
|
+
IsConfigRequired,
|
|
12
|
+
MutationEndpoint,
|
|
13
|
+
QueryEndpoint,
|
|
14
|
+
TypedApiFunction,
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build query key from endpoint and config
|
|
19
|
+
*/
|
|
20
|
+
function buildQueryKey<Paths, T extends QueryEndpoint<Paths>>(
|
|
21
|
+
endpoint: T,
|
|
22
|
+
config?: FilteredRequestConfig<Paths, T>,
|
|
23
|
+
): unknown[] {
|
|
24
|
+
const key: unknown[] = [endpoint];
|
|
25
|
+
|
|
26
|
+
if (config && 'params' in config && config.params) {
|
|
27
|
+
key.push({ params: config.params });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (config && 'query' in config && config.query) {
|
|
31
|
+
key.push({ query: config.query });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return key;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for creating endpoint-based hooks
|
|
39
|
+
*/
|
|
40
|
+
export interface CreateEndpointHooksOptions {
|
|
41
|
+
queryClient?: QueryClient;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hook options type that conditionally requires config
|
|
46
|
+
*/
|
|
47
|
+
type UseQueryArgs<Paths, T extends QueryEndpoint<Paths>> = IsConfigRequired<
|
|
48
|
+
Paths,
|
|
49
|
+
T
|
|
50
|
+
> extends true
|
|
51
|
+
? [
|
|
52
|
+
config: FilteredRequestConfig<Paths, T>,
|
|
53
|
+
options?: Omit<
|
|
54
|
+
UseQueryOptions<ExtractEndpointResponse<Paths, T>, Error>,
|
|
55
|
+
'queryKey' | 'queryFn'
|
|
56
|
+
>,
|
|
57
|
+
]
|
|
58
|
+
: [
|
|
59
|
+
config?: FilteredRequestConfig<Paths, T>,
|
|
60
|
+
options?: Omit<
|
|
61
|
+
UseQueryOptions<ExtractEndpointResponse<Paths, T>, Error>,
|
|
62
|
+
'queryKey' | 'queryFn'
|
|
63
|
+
>,
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Endpoint-based React Query hooks
|
|
68
|
+
*/
|
|
69
|
+
export interface EndpointHooks<Paths> {
|
|
70
|
+
/**
|
|
71
|
+
* Use query hook for GET endpoints.
|
|
72
|
+
* Config is required when endpoint has path params.
|
|
73
|
+
*/
|
|
74
|
+
useQuery: <T extends QueryEndpoint<Paths>>(
|
|
75
|
+
endpoint: T,
|
|
76
|
+
...args: UseQueryArgs<Paths, T>
|
|
77
|
+
) => ReturnType<typeof useQuery<ExtractEndpointResponse<Paths, T>, Error>>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Use mutation hook for POST, PUT, PATCH, DELETE endpoints.
|
|
81
|
+
* Config with params/body is passed to mutate().
|
|
82
|
+
*/
|
|
83
|
+
useMutation: <T extends MutationEndpoint<Paths>>(
|
|
84
|
+
endpoint: T,
|
|
85
|
+
options?: Omit<
|
|
86
|
+
UseMutationOptions<
|
|
87
|
+
ExtractEndpointResponse<Paths, T>,
|
|
88
|
+
Error,
|
|
89
|
+
FilteredRequestConfig<Paths, T>
|
|
90
|
+
>,
|
|
91
|
+
'mutationFn'
|
|
92
|
+
>,
|
|
93
|
+
) => ReturnType<
|
|
94
|
+
typeof useMutation<
|
|
95
|
+
ExtractEndpointResponse<Paths, T>,
|
|
96
|
+
Error,
|
|
97
|
+
FilteredRequestConfig<Paths, T>
|
|
98
|
+
>
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Build a query key for manual cache operations
|
|
103
|
+
*/
|
|
104
|
+
buildQueryKey: <T extends QueryEndpoint<Paths>>(
|
|
105
|
+
endpoint: T,
|
|
106
|
+
config?: FilteredRequestConfig<Paths, T>,
|
|
107
|
+
) => unknown[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create endpoint-based React Query hooks from a typed fetcher.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const fetcher = createAuthAwareFetcher<paths>({ ... });
|
|
116
|
+
* const hooks = createEndpointHooks<paths>(fetcher);
|
|
117
|
+
*
|
|
118
|
+
* // In a component
|
|
119
|
+
* const { data } = hooks.useQuery('GET /users/{id}', { params: { id: '123' } });
|
|
120
|
+
*
|
|
121
|
+
* const mutation = hooks.useMutation('POST /users');
|
|
122
|
+
* await mutation.mutateAsync({ body: { name: 'John' } });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function createEndpointHooks<Paths>(
|
|
126
|
+
fetcher: TypedApiFunction<Paths>,
|
|
127
|
+
options: CreateEndpointHooksOptions = {},
|
|
128
|
+
): EndpointHooks<Paths> {
|
|
129
|
+
return {
|
|
130
|
+
useQuery: <T extends QueryEndpoint<Paths>>(
|
|
131
|
+
endpoint: T,
|
|
132
|
+
...args: UseQueryArgs<Paths, T>
|
|
133
|
+
) => {
|
|
134
|
+
// Parse args - config is first, options is second
|
|
135
|
+
const [config, queryOptions] = args as [
|
|
136
|
+
FilteredRequestConfig<Paths, T> | undefined,
|
|
137
|
+
(
|
|
138
|
+
| Omit<
|
|
139
|
+
UseQueryOptions<ExtractEndpointResponse<Paths, T>, Error>,
|
|
140
|
+
'queryKey' | 'queryFn'
|
|
141
|
+
>
|
|
142
|
+
| undefined
|
|
143
|
+
),
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
const queryKey = buildQueryKey(endpoint, config);
|
|
147
|
+
|
|
148
|
+
const memoizedOptions = useMemo(
|
|
149
|
+
() => ({
|
|
150
|
+
queryKey,
|
|
151
|
+
queryFn: () =>
|
|
152
|
+
fetcher(
|
|
153
|
+
endpoint as Parameters<typeof fetcher>[0],
|
|
154
|
+
config as Parameters<typeof fetcher>[1],
|
|
155
|
+
),
|
|
156
|
+
...queryOptions,
|
|
157
|
+
}),
|
|
158
|
+
[
|
|
159
|
+
queryKey.join(','),
|
|
160
|
+
endpoint,
|
|
161
|
+
JSON.stringify(config),
|
|
162
|
+
JSON.stringify(queryOptions),
|
|
163
|
+
],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return useQuery<ExtractEndpointResponse<Paths, T>, Error>(
|
|
167
|
+
memoizedOptions,
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
useMutation: <T extends MutationEndpoint<Paths>>(
|
|
172
|
+
endpoint: T,
|
|
173
|
+
mutationOptions?: Omit<
|
|
174
|
+
UseMutationOptions<
|
|
175
|
+
ExtractEndpointResponse<Paths, T>,
|
|
176
|
+
Error,
|
|
177
|
+
FilteredRequestConfig<Paths, T>
|
|
178
|
+
>,
|
|
179
|
+
'mutationFn'
|
|
180
|
+
>,
|
|
181
|
+
) => {
|
|
182
|
+
const memoizedOptions = useMemo(
|
|
183
|
+
() => ({
|
|
184
|
+
mutationFn: (config: FilteredRequestConfig<Paths, T>) =>
|
|
185
|
+
fetcher(
|
|
186
|
+
endpoint as Parameters<typeof fetcher>[0],
|
|
187
|
+
config as Parameters<typeof fetcher>[1],
|
|
188
|
+
),
|
|
189
|
+
...mutationOptions,
|
|
190
|
+
}),
|
|
191
|
+
[endpoint, JSON.stringify(mutationOptions)],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return useMutation<
|
|
195
|
+
ExtractEndpointResponse<Paths, T>,
|
|
196
|
+
Error,
|
|
197
|
+
FilteredRequestConfig<Paths, T>
|
|
198
|
+
>(memoizedOptions);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
buildQueryKey,
|
|
202
|
+
};
|
|
203
|
+
}
|
package/src/infer.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Endpoint,
|
|
3
|
+
EndpointSchemas,
|
|
4
|
+
} from '@geekmidas/constructs/endpoints';
|
|
5
|
+
import type { HttpMethod } from '@geekmidas/constructs/types';
|
|
6
|
+
import type { InferStandardSchema } from '@geekmidas/schema';
|
|
7
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Infers path parameters from a route string as an object
|
|
11
|
+
* @example '/users/{id}/posts/{postId}' -> { id: string, postId: string }
|
|
12
|
+
*/
|
|
13
|
+
type InferPathParams<TRoute extends string> =
|
|
14
|
+
TRoute extends `${string}{${infer Param}}${infer Rest}`
|
|
15
|
+
? { [K in Param]: string } & InferPathParams<Rest>
|
|
16
|
+
: {};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts an HTTP method to lowercase for TypedFetcher compatibility
|
|
20
|
+
*/
|
|
21
|
+
type LowercaseMethod<T extends HttpMethod> = Lowercase<T>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Infers route-level parameters (path params)
|
|
25
|
+
*/
|
|
26
|
+
type InferRouteParameters<TRoute extends string> =
|
|
27
|
+
InferPathParams<TRoute> extends Record<string, never>
|
|
28
|
+
? {}
|
|
29
|
+
: {
|
|
30
|
+
parameters: {
|
|
31
|
+
path: InferPathParams<TRoute>;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Infers operation-level parameters (query params)
|
|
37
|
+
*/
|
|
38
|
+
type InferOperationParameters<TInput extends EndpointSchemas> = TInput extends {
|
|
39
|
+
query: infer Q;
|
|
40
|
+
}
|
|
41
|
+
? {
|
|
42
|
+
parameters: {
|
|
43
|
+
query: InferStandardSchema<Q>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
: {};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Infers the operation object compatible with TypedFetcher
|
|
50
|
+
*/
|
|
51
|
+
type InferOperation<
|
|
52
|
+
TInput extends EndpointSchemas,
|
|
53
|
+
TOutput extends StandardSchemaV1 | undefined,
|
|
54
|
+
> = InferOperationParameters<TInput> & {
|
|
55
|
+
requestBody?: TInput extends { body: infer B }
|
|
56
|
+
? {
|
|
57
|
+
content: {
|
|
58
|
+
'application/json': InferStandardSchema<B>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
: never;
|
|
62
|
+
responses: {
|
|
63
|
+
200: {
|
|
64
|
+
content: TOutput extends StandardSchemaV1
|
|
65
|
+
? {
|
|
66
|
+
'application/json': InferStandardSchema<TOutput>;
|
|
67
|
+
}
|
|
68
|
+
: never;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Infers the TypedFetcher-compatible paths structure from a single endpoint
|
|
75
|
+
*
|
|
76
|
+
* This generates a structure compatible with @geekmidas/client TypedFetcher,
|
|
77
|
+
* allowing you to create a typed client directly from endpoint definitions
|
|
78
|
+
* without needing OpenAPI JSON + codegen.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* import { e } from '@geekmidas/constructs';
|
|
83
|
+
* import { createTypedFetcher, type InferOpenApiFromEndpoint } from '@geekmidas/client';
|
|
84
|
+
* import { z } from 'zod';
|
|
85
|
+
*
|
|
86
|
+
* const endpoint = e
|
|
87
|
+
* .get('/users/{id}')
|
|
88
|
+
* .params(z.object({ id: z.string() }))
|
|
89
|
+
* .output(z.object({ id: z.string(), name: z.string() }))
|
|
90
|
+
* .handle(async ({ params }) => ({ id: params.id, name: 'John' }));
|
|
91
|
+
*
|
|
92
|
+
* type Paths = InferOpenApiFromEndpoint<typeof endpoint>['paths'];
|
|
93
|
+
* const client = createTypedFetcher<Paths>({ baseURL: 'http://localhost:3000' });
|
|
94
|
+
* const user = await client('GET /users/{id}', { params: { id: '123' } });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export type InferOpenApiFromEndpoint<T> = T extends Endpoint<
|
|
98
|
+
infer TRoute,
|
|
99
|
+
infer TMethod,
|
|
100
|
+
infer TInput,
|
|
101
|
+
infer TOutput,
|
|
102
|
+
any,
|
|
103
|
+
any,
|
|
104
|
+
any
|
|
105
|
+
>
|
|
106
|
+
? {
|
|
107
|
+
paths: {
|
|
108
|
+
[K in TRoute]: InferRouteParameters<TRoute> & {
|
|
109
|
+
[M in LowercaseMethod<TMethod>]: InferOperation<TInput, TOutput>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
: never;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Infers TypedFetcher-compatible paths structure from multiple endpoints
|
|
117
|
+
*
|
|
118
|
+
* Merges multiple endpoint definitions into a single paths object that can be
|
|
119
|
+
* used with @geekmidas/client TypedFetcher for fully type-safe API calls.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* import { e } from '@geekmidas/constructs';
|
|
124
|
+
* import { createTypedFetcher, type InferOpenApi } from '@geekmidas/client';
|
|
125
|
+
* import { z } from 'zod';
|
|
126
|
+
*
|
|
127
|
+
* // Define endpoints
|
|
128
|
+
* const getUserEndpoint = e
|
|
129
|
+
* .get('/users/{id}')
|
|
130
|
+
* .params(z.object({ id: z.string() }))
|
|
131
|
+
* .output(z.object({ id: z.string(), name: z.string() }))
|
|
132
|
+
* .handle(async ({ params }) => ({ id: params.id, name: 'John' }));
|
|
133
|
+
*
|
|
134
|
+
* const createUserEndpoint = e
|
|
135
|
+
* .post('/users')
|
|
136
|
+
* .body(z.object({ name: z.string() }))
|
|
137
|
+
* .output(z.object({ id: z.string(), name: z.string() }))
|
|
138
|
+
* .handle(async ({ body }) => ({ id: '123', name: body.name }));
|
|
139
|
+
*
|
|
140
|
+
* // Export for client
|
|
141
|
+
* export const endpoints = [getUserEndpoint, createUserEndpoint] as const;
|
|
142
|
+
* export type Paths = InferOpenApi<typeof endpoints>['paths'];
|
|
143
|
+
*
|
|
144
|
+
* // Client usage
|
|
145
|
+
* import type { Paths } from './endpoints';
|
|
146
|
+
* const client = createTypedFetcher<Paths>({ baseURL: 'http://localhost:3000' });
|
|
147
|
+
*
|
|
148
|
+
* const user = await client('GET /users/{id}', { params: { id: '123' } });
|
|
149
|
+
* const newUser = await client('POST /users', { body: { name: 'Jane' } });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export type InferOpenApi<TEndpoints extends readonly any[]> =
|
|
153
|
+
TEndpoints extends readonly [infer First, ...infer Rest]
|
|
154
|
+
? InferOpenApiFromEndpoint<First> extends { paths: infer P1 }
|
|
155
|
+
? Rest extends []
|
|
156
|
+
? { paths: P1 }
|
|
157
|
+
: InferOpenApi<Rest> extends { paths: infer P2 }
|
|
158
|
+
? { paths: P1 & P2 }
|
|
159
|
+
: { paths: P1 }
|
|
160
|
+
: InferOpenApi<Rest>
|
|
161
|
+
: { paths: {} };
|
package/src/types.ts
CHANGED
|
@@ -130,14 +130,75 @@ export type ExtractEndpointConfig<
|
|
|
130
130
|
: never
|
|
131
131
|
: never;
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Build a request config type where:
|
|
135
|
+
* - `params` is required if the endpoint has path parameters
|
|
136
|
+
* - `body` is required if the endpoint has a request body
|
|
137
|
+
* - `query` and `headers` are always optional
|
|
138
|
+
*/
|
|
139
|
+
export type FilteredRequestConfig<
|
|
140
|
+
Paths,
|
|
141
|
+
T extends EndpointString,
|
|
142
|
+
> = T extends `${infer Method} ${infer Route}`
|
|
143
|
+
? Route extends OpenAPIRoutes<Paths>
|
|
144
|
+
? Lowercase<Method> extends ExtractMethod<Paths, Route>
|
|
145
|
+
? BuildRequestConfig<
|
|
146
|
+
ExtractPathParams<Paths, Route>,
|
|
147
|
+
ExtractQueryParams<Paths, Route, Lowercase<Method>>,
|
|
148
|
+
ExtractRequestBody<Paths, Route, Lowercase<Method>>
|
|
149
|
+
>
|
|
150
|
+
: never
|
|
151
|
+
: never
|
|
152
|
+
: never;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Helper to build request config with correct required/optional fields
|
|
156
|
+
*/
|
|
157
|
+
type BuildRequestConfig<TParams, TQuery, TBody> = SimplifyIntersection<
|
|
158
|
+
// params: required if not never
|
|
159
|
+
(TParams extends never ? {} : { params: TParams }) &
|
|
160
|
+
// body: required if not never
|
|
161
|
+
(TBody extends never ? {} : { body: TBody }) &
|
|
162
|
+
// query: optional if not never
|
|
163
|
+
(TQuery extends never ? {} : { query?: TQuery }) & {
|
|
164
|
+
// headers: always optional
|
|
165
|
+
headers?: Record<string, string>;
|
|
166
|
+
}
|
|
167
|
+
>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Simplify intersection types for better IDE display
|
|
171
|
+
*/
|
|
172
|
+
type SimplifyIntersection<T> = { [K in keyof T]: T[K] };
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if the config object is required (has any required fields)
|
|
176
|
+
*/
|
|
177
|
+
export type IsConfigRequired<
|
|
178
|
+
Paths,
|
|
179
|
+
T extends EndpointString,
|
|
180
|
+
> = T extends `${infer Method} ${infer Route}`
|
|
181
|
+
? Route extends OpenAPIRoutes<Paths>
|
|
182
|
+
? Lowercase<Method> extends ExtractMethod<Paths, Route>
|
|
183
|
+
? ExtractPathParams<Paths, Route> extends never
|
|
184
|
+
? ExtractRequestBody<Paths, Route, Lowercase<Method>> extends never
|
|
185
|
+
? false
|
|
186
|
+
: true
|
|
187
|
+
: true
|
|
188
|
+
: false
|
|
189
|
+
: false
|
|
190
|
+
: false;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Typed function signature for the API client.
|
|
194
|
+
* Config is required when endpoint has path params or body.
|
|
195
|
+
*/
|
|
196
|
+
export type TypedApiFunction<Paths> = <T extends TypedEndpoint<Paths>>(
|
|
197
|
+
endpoint: T,
|
|
198
|
+
...args: IsConfigRequired<Paths, T> extends true
|
|
199
|
+
? [config: FilteredRequestConfig<Paths, T>]
|
|
200
|
+
: [config?: FilteredRequestConfig<Paths, T>]
|
|
201
|
+
) => Promise<ExtractEndpointResponse<Paths, T>>;
|
|
141
202
|
|
|
142
203
|
export interface FetcherOptions {
|
|
143
204
|
baseURL?: string;
|