11import { BaseBuildModuleReturn , BuildModuleReturn } from '@/api/lib/module' ;
22import { Route } from '@/api/lib/route' ;
33import { 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.
5166type 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
79146type 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
89156type 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
134215export 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} ) ( ) ;
0 commit comments