Skip to content

Commit 708ec47

Browse files
committed
perf: Add args into fetcher
1 parent 873cea3 commit 708ec47

File tree

4 files changed

+166
-40
lines changed

4 files changed

+166
-40
lines changed

packages/vitnode/src/test/elo.ts

Lines changed: 154 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { BaseBuildModuleReturn, BuildModuleReturn } from '@/api/lib/module';
22
import { Route } from '@/api/lib/route';
33
import { usersModule } from '@/api/modules/users/users.module';
4+
import { RouteConfig } from '@hono/zod-openapi';
5+
import { z } from 'zod';
46

57
// --- Core Type Definitions ---
68

@@ -46,8 +48,21 @@ type FindModuleNested<
4648
: // If Path is empty, it implies we are looking for the module M itself
4749
M;
4850

51+
// --- Simplified Module Path Types ---
52+
53+
// Creates a string union type of all possible module paths up to 3 levels deep
54+
type GetModulePaths<
55+
MainModule extends string,
56+
Modules extends readonly ModuleSpec[],
57+
> =
58+
| `${MainModule}/${Modules[number]['name']}/${Extract<
59+
Modules[number]['modules'],
60+
readonly ModuleSpec[]
61+
>[number]['name']}`
62+
| `${MainModule}/${Modules[number]['name']}`
63+
| MainModule;
64+
4965
// Helper to get the target module specification based on the module path string
50-
// It handles the base module case and nested module cases.
5166
type GetTargetModule<
5267
ModulePath extends string,
5368
MainModuleName extends string,
@@ -73,9 +88,61 @@ type ExtractMethodForPath<
7388
P extends string,
7489
> = Extract<M['routes'][number], { route: { path: P } }>['route']['method'];
7590

76-
// --- Derived Types for Fetcher ---
91+
// --- Type extraction utilities ---
92+
93+
// Helper to extract types from Zod schemas that might be in different structures
94+
type ExtractZodType<T> = T extends z.ZodTypeAny ? z.infer<T> : never;
95+
96+
// Infers the input type for a specific part of the route config (body, query, params)
97+
type InferInputType<
98+
RouteCfg extends RouteConfig,
99+
Part extends 'body' | 'params' | 'query',
100+
> = Part extends 'body'
101+
? RouteCfg extends {
102+
request: {
103+
body: { content: { 'application/json': { schema: infer S } } };
104+
};
105+
}
106+
? ExtractZodType<S>
107+
: RouteCfg extends { request: { body: { schema?: infer S } } }
108+
? ExtractZodType<S>
109+
: undefined
110+
: Part extends 'query'
111+
? RouteCfg extends { request: { query: infer S } }
112+
? ExtractZodType<S>
113+
: undefined
114+
: Part extends 'params'
115+
? RouteCfg extends { request: { params: infer S } }
116+
? ExtractZodType<S>
117+
: undefined
118+
: never;
119+
120+
// --- Route Configuration Extraction ---
121+
122+
// Find the route configuration for a specific module path, route path, and method
123+
type FindRouteConfig<
124+
M extends { routes: readonly Route[] },
125+
P extends string,
126+
Method extends string,
127+
> = Extract<
128+
M['routes'][number],
129+
{ route: { method: Method; path: P } }
130+
>['route'];
131+
132+
// Constructs the final Args type based on the inferred input types
133+
type BuildArgsType<RouteCfg extends RouteConfig> = {
134+
// Use key remapping to filter out keys where the inferred type is undefined
135+
[K in 'body' | 'params' | 'query' as InferInputType<
136+
RouteCfg,
137+
K
138+
> extends undefined
139+
? never
140+
: K]: InferInputType<RouteCfg, K>;
141+
};
77142

78-
// Gets all valid path strings for a given module path (e.g., "users" or "users/sso")
143+
// --- Fetcher Types ---
144+
145+
// Gets all valid path strings for a given module path
79146
type GetValidPathsForModule<
80147
ModulePath extends string,
81148
MainModuleName extends string,
@@ -85,7 +152,7 @@ type GetValidPathsForModule<
85152
GetTargetModule<ModulePath, MainModuleName, MainRoutes, SubModules>
86153
>;
87154

88-
// Gets the valid method (lowercase) for a given module path and a specific path within that module
155+
// Gets the valid method for a given module path and route path
89156
type GetValidMethodForPath<
90157
ModulePath extends string,
91158
Path extends string,
@@ -98,79 +165,126 @@ type GetValidMethodForPath<
98165
GetTargetModule<ModulePath, MainModuleName, MainRoutes, SubModules>,
99166
Path
100167
>,
101-
string // Ensure we only get string methods
168+
string
102169
>
103170
>;
104171

105-
// --- Fetcher Function Definition ---
172+
// --- Fetcher Parameters ---
106173

107-
// Define the structure for the fetcher parameters, using the derived types for constraints
108-
interface FetcherParams<
109-
// Generic parameters from BuildModuleReturn
110-
P extends string,
174+
// Define the base parameters without args
175+
interface BaseFetcherParams<
111176
M extends string,
112177
Routes extends Route[],
113-
Modules extends BaseBuildModuleReturn<P>[],
114-
// The specific module path string provided by the user (e.g., "users/sso")
115-
// This complex union type accurately constrains valid module paths.
116-
ModuleName extends
117-
| `${M}/${Modules[number]['name']}/${Extract<
118-
Modules[number]['modules'],
119-
readonly BaseBuildModuleReturn<P>[]
120-
>[number]['name']}` // Second level sub-module
121-
| `${M}/${Modules[number]['name']}` // First level sub-module (e.g., "users/sso")
122-
// Add support for deeper nesting if necessary:
123-
| M, // Base module name (e.g., "users")
124-
// The specific path string selected within the chosen module
178+
Modules extends BaseBuildModuleReturn[],
179+
ModuleName extends GetModulePaths<M, Modules>,
125180
SelectedPath extends GetValidPathsForModule<ModuleName, M, Routes, Modules>,
126181
> {
127-
input?: unknown; // TODO: Define input type based on the route if possible
128182
method: GetValidMethodForPath<ModuleName, SelectedPath, M, Routes, Modules>;
129183
module: ModuleName;
130184
path: SelectedPath;
131185
}
132186

133-
// The fetcher function signature
187+
// Use conditional type with intersection to define FetcherParams
188+
type FetcherParams<
189+
// Module definition parameters
190+
M extends string,
191+
Routes extends Route[],
192+
Modules extends BaseBuildModuleReturn[],
193+
// Dynamic parameters based on user selection
194+
ModuleName extends GetModulePaths<M, Modules>,
195+
SelectedPath extends GetValidPathsForModule<ModuleName, M, Routes, Modules>,
196+
// Extract the route configuration and build the args type
197+
RouteConfig extends FindRouteConfig<
198+
GetTargetModule<ModuleName, M, Routes, Modules>,
199+
SelectedPath,
200+
GetValidMethodForPath<ModuleName, SelectedPath, M, Routes, Modules>
201+
> = FindRouteConfig<
202+
GetTargetModule<ModuleName, M, Routes, Modules>,
203+
SelectedPath,
204+
GetValidMethodForPath<ModuleName, SelectedPath, M, Routes, Modules>
205+
>,
206+
ArgsType extends BuildArgsType<RouteConfig> = BuildArgsType<RouteConfig>,
207+
> = BaseFetcherParams<M, Routes, Modules, ModuleName, SelectedPath> & // Intersect with base
208+
(keyof ArgsType extends never
209+
? { args?: undefined } // Args optional and undefined if ArgsType is empty
210+
: { args: ArgsType }); // Args required if ArgsType is not empty
211+
212+
// --- Fetcher Function ---
213+
214+
// Simplified fetcher with fewer generic type parameters
134215
export function fetcher<
135-
// Generic parameters matching BuildModuleReturn
136-
P extends string,
137216
M extends string,
138217
Routes extends Route[],
139-
Modules extends BaseBuildModuleReturn<P>[],
140-
// Constrain ModuleName to valid possibilities based on the module structure
141-
ModuleName extends
142-
| `${M}/${Modules[number]['name']}/${Extract<
143-
Modules[number]['modules'],
144-
readonly BaseBuildModuleReturn<P>[]
145-
>[number]['name']}`
146-
| `${M}/${Modules[number]['name']}`
147-
| M,
148-
// SelectedPath is constrained based on the chosen ModuleName
218+
Modules extends BaseBuildModuleReturn[],
219+
ModuleName extends GetModulePaths<M, Modules>,
149220
SelectedPath extends GetValidPathsForModule<ModuleName, M, Routes, Modules>,
150221
>(
151-
_moduleInput: BuildModuleReturn<P, M, Routes, Modules>, // Mark as unused
152-
params: FetcherParams<P, M, Routes, Modules, ModuleName, SelectedPath>,
222+
_moduleInput: BuildModuleReturn<string, M, Routes, Modules>,
223+
params: FetcherParams<M, Routes, Modules, ModuleName, SelectedPath>,
153224
): void {
154225
// Function implementation would go here
155226
void params; // Mark as unused for now
156227
}
157228

229+
// Test cases
158230
(() => {
231+
// Assuming /sign_in requires a body, this would now potentially error if args is missing
232+
// If it doesn't require args, this is fine.
159233
fetcher(usersModule, {
160-
path: '/test',
161-
method: 'get',
234+
path: '/sign_in',
235+
method: 'post',
162236
module: 'users',
237+
args: {
238+
body: {
239+
email: 'string',
240+
password: 'string',
241+
},
242+
},
163243
});
164244

165245
fetcher(usersModule, {
166246
path: '/{providerId}',
167247
method: 'post',
168248
module: 'users/sso',
249+
args: {
250+
// args is required because params exist
251+
params: {
252+
providerId: 'github',
253+
},
254+
},
169255
});
170256

171257
fetcher(usersModule, {
172258
path: '/{providerId}/callback',
173259
method: 'get',
174260
module: 'users/sso',
261+
args: {
262+
// args is required because params and query exist
263+
params: {
264+
providerId: 'github',
265+
},
266+
query: {
267+
code: 'some-code',
268+
state: 'some-state',
269+
},
270+
},
271+
});
272+
273+
// Assuming /test does not require args, this is fine.
274+
fetcher(usersModule, {
275+
path: '/test',
276+
method: 'post',
277+
module: 'users/sso/test',
278+
// args is optional here if ArgsType is empty
279+
});
280+
281+
// Should trigger error if required args are missing
282+
/* Error example (assuming /sign_in requires args):
283+
fetcher(usersModule, {
284+
path: '/sign_in',
285+
method: 'post',
286+
module: 'users',
287+
// Missing required 'args' property
175288
});
289+
*/
176290
})();

packages/vitnode/src/test/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,5 @@ export const test = new OpenAPIHono()
8080
.route('/something', test1);
8181

8282
const client = hc<typeof test>('http://localhost:3000/api/core/test');
83+
84+
await client.something.test123[':test'].$get({});

plugins/blog/src/modules/categories/route.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ export const categoriesRoute = buildRoute({
55
route: {
66
method: 'get',
77
path: '/',
8+
request: {
9+
query: z.object({
10+
test: z.string(),
11+
}),
12+
},
813
responses: {
914
200: {
1015
content: {

plugins/blog/src/modules/categories/test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ export const test = () => {
77
module: 'categories',
88
path: '/',
99
method: 'get',
10+
args: {
11+
query: {
12+
test: 'test',
13+
},
14+
},
1015
});
1116
};

0 commit comments

Comments
 (0)