@@ -18,13 +18,19 @@ type StrictParams<T> = {
1818type Identity = Awaited < ReturnType < typeof getUserInformation > > ;
1919
2020// Base configuration type for protected routes
21- type BaseProtectedConfig < T , P extends z . ZodSchema | undefined = undefined > = {
21+ type BaseProtectedConfig <
22+ T ,
23+ P extends z . ZodSchema | undefined = undefined ,
24+ Q extends z . ZodSchema | undefined = undefined ,
25+ > = {
2226 permissions ?: PermissionCheck ;
2327 paramValidation ?: P ;
28+ queryValidation ?: Q ;
2429 function : (
2530 args : Omit < LoaderFunctionArgs , "params" > & {
2631 identity : Identity ;
2732 params : P extends z . ZodSchema ? StrictParams < z . infer < P > > : null ;
33+ query : Q extends z . ZodSchema ? StrictParams < z . infer < Q > > : null ;
2834 }
2935 ) => T ;
3036} ;
@@ -33,39 +39,63 @@ type BaseProtectedConfig<T, P extends z.ZodSchema | undefined = undefined> = {
3339type ProtectedLoaderConfig <
3440 T = any ,
3541 P extends z . ZodSchema | undefined = undefined ,
36- > = BaseProtectedConfig < T , P > ;
42+ Q extends z . ZodSchema | undefined = undefined ,
43+ > = BaseProtectedConfig < T , P , Q > ;
3744
3845// Action specific configuration
3946type ProtectedActionConfig <
4047 T = any ,
4148 P extends z . ZodSchema | undefined = undefined ,
42- > = BaseProtectedConfig < T , P > ;
49+ Q extends z . ZodSchema | undefined = undefined ,
50+ > = BaseProtectedConfig < T , P , Q > ;
4351
4452/**
4553 * Creates a protected loader function that validates permissions and parameters
4654 */
47- export function createProtectedLoader < T , P extends z . ZodSchema | undefined = undefined > (
48- config : ProtectedLoaderConfig < T , P >
49- ) {
55+ export function createProtectedLoader <
56+ T ,
57+ P extends z . ZodSchema | undefined = undefined ,
58+ Q extends z . ZodSchema | undefined = undefined ,
59+ > ( config : ProtectedLoaderConfig < T , P , Q > ) {
5060 return async ( args : LoaderFunctionArgs ) => {
5161 const parsedParams = validateParams ( args . params , config . paramValidation ) ;
62+ const parsedQuery = validateQueryParams (
63+ new URL ( args . request . url ) . searchParams ,
64+ config . queryValidation
65+ ) ;
5266 const identity = await validateIdentity ( args . request , config . permissions ) ;
5367 const { params : _ , ...rest } = args ;
54- return await config . function ( { ...rest , identity, params : parsedParams . data } ) ;
68+ return await config . function ( {
69+ ...rest ,
70+ identity,
71+ params : parsedParams . data ,
72+ query : parsedQuery . data ,
73+ } ) ;
5574 } ;
5675}
5776
5877/**
5978 * Creates a protected action function that validates permissions and parameters
6079 */
61- export function createProtectedAction < T , P extends z . ZodSchema | undefined = undefined > (
62- config : ProtectedActionConfig < T , P >
63- ) {
80+ export function createProtectedAction <
81+ T ,
82+ P extends z . ZodSchema | undefined = undefined ,
83+ Q extends z . ZodSchema | undefined = undefined ,
84+ > ( config : ProtectedActionConfig < T , P , Q > ) {
6485 return async ( args : ActionFunctionArgs ) => {
6586 const parsedParams = validateParams ( args . params , config . paramValidation ) ;
87+ const parsedQuery = validateQueryParams (
88+ new URL ( args . request . url ) . searchParams ,
89+ config . queryValidation
90+ ) ;
6691 const identity = await validateIdentity ( args . request , config . permissions ) ;
6792 const { params : _ , ...rest } = args ;
68- return await config . function ( { ...rest , identity, params : parsedParams . data } ) ;
93+ return await config . function ( {
94+ ...rest ,
95+ identity,
96+ params : parsedParams . data ,
97+ query : parsedQuery . data ,
98+ } ) ;
6999 } ;
70100}
71101
@@ -93,3 +123,22 @@ function validateParams(params: any, schema?: z.ZodSchema) {
93123
94124 return { data : parsed . data } ;
95125}
126+
127+ function validateQueryParams ( searchParams : URLSearchParams , schema ?: z . ZodSchema ) {
128+ if ( ! schema ) {
129+ return { data : null } ;
130+ }
131+
132+ // Convert URLSearchParams to a plain object
133+ const params : Record < string , string > = { } ;
134+ searchParams . forEach ( ( value , key ) => {
135+ params [ key ] = value ;
136+ } ) ;
137+
138+ const parsed = schema . safeParse ( params ) ;
139+ if ( ! parsed . success ) {
140+ throw new Error ( parsed . error . message ) ;
141+ }
142+
143+ return { data : parsed . data } ;
144+ }
0 commit comments