@@ -38,6 +38,7 @@ import type {
3838 FindUniqueArgs ,
3939 GroupByArgs ,
4040 GroupByResult ,
41+ ProcedureFunc ,
4142 QueryOptions ,
4243 SelectSubset ,
4344 SimplifiedPlainResult ,
@@ -61,6 +62,25 @@ import type {
6162} from './common/types' ;
6263export type { FetchFn } from '@zenstackhq/client-helpers/fetch' ;
6364
65+ type ExtractProcedures < Schema extends SchemaDef > = Schema extends { procedures : Record < string , any > }
66+ ? NonNullable < Schema [ 'procedures' ] >
67+ : never ;
68+
69+ type ProcedureArgsTuple < Schema extends SchemaDef , Name extends keyof ExtractProcedures < Schema > > = Parameters <
70+ ProcedureFunc < Schema , ExtractProcedures < Schema > [ Name ] >
71+ > ;
72+
73+ type ProcedureReturn < Schema extends SchemaDef , Name extends keyof ExtractProcedures < Schema > > = Awaited < ReturnType <
74+ ProcedureFunc < Schema , ExtractProcedures < Schema > [ Name ] >
75+ > > ;
76+
77+ type ProcedurePayload < Schema extends SchemaDef , Name extends keyof ExtractProcedures < Schema > > =
78+ ProcedureArgsTuple < Schema , Name > extends [ ]
79+ ? undefined
80+ : ProcedureArgsTuple < Schema , Name > extends [ infer A ]
81+ ? A
82+ : ProcedureArgsTuple < Schema , Name > ;
83+
6484/**
6585 * React context for query settings.
6686 */
@@ -133,8 +153,56 @@ export type ModelMutationModelResult<
133153
134154export type ClientHooks < Schema extends SchemaDef , Options extends QueryOptions < Schema > = QueryOptions < Schema > > = {
135155 [ Model in GetModels < Schema > as `${Uncapitalize < Model > } `] : ModelQueryHooks < Schema , Model , Options > ;
156+ } & ProcedureHooks < Schema > ;
157+
158+ type ProcedureHookGroup < Schema extends SchemaDef > = {
159+ [ Name in keyof ExtractProcedures < Schema > ] : ExtractProcedures < Schema > [ Name ] extends { mutation : true }
160+ ? {
161+ useMutation (
162+ options ?: Omit <
163+ UseMutationOptions < ProcedureReturn < Schema , Name > , DefaultError , ProcedurePayload < Schema , Name > > ,
164+ 'mutationFn'
165+ > &
166+ QueryContext ,
167+ ) : UseMutationResult < ProcedureReturn < Schema , Name > , DefaultError , ProcedurePayload < Schema , Name > > ;
168+ }
169+ : {
170+ useQuery (
171+ args ?: ProcedurePayload < Schema , Name > ,
172+ options ?: ModelQueryOptions < ProcedureReturn < Schema , Name > > ,
173+ ) : ModelQueryResult < ProcedureReturn < Schema , Name > > ;
174+
175+ useSuspenseQuery (
176+ args ?: ProcedurePayload < Schema , Name > ,
177+ options ?: ModelSuspenseQueryOptions < ProcedureReturn < Schema , Name > > ,
178+ ) : ModelSuspenseQueryResult < ProcedureReturn < Schema , Name > > ;
179+
180+ useInfiniteQuery (
181+ args ?: ProcedurePayload < Schema , Name > ,
182+ options ?: ModelInfiniteQueryOptions < ProcedureReturn < Schema , Name > > ,
183+ ) : ModelInfiniteQueryResult < InfiniteData < ProcedureReturn < Schema , Name > > > ;
184+
185+ useSuspenseInfiniteQuery (
186+ args ?: ProcedurePayload < Schema , Name > ,
187+ options ?: ModelSuspenseInfiniteQueryOptions < ProcedureReturn < Schema , Name > > ,
188+ ) : ModelSuspenseInfiniteQueryResult < InfiniteData < ProcedureReturn < Schema , Name > > > ;
189+ } ;
136190} ;
137191
192+ export type ProcedureHooks < Schema extends SchemaDef > = Schema extends { procedures : Record < string , any > }
193+ ? {
194+ /**
195+ * Preferred procedures API.
196+ */
197+ $procs : ProcedureHookGroup < Schema > ;
198+
199+ /**
200+ * Backward-compatible procedures API.
201+ */
202+ $procedures : ProcedureHookGroup < Schema > ;
203+ }
204+ : { } ;
205+
138206// Note that we can potentially use TypeScript's mapped type to directly map from ORM contract, but that seems
139207// to significantly slow down tsc performance ...
140208export type ModelQueryHooks <
@@ -263,7 +331,7 @@ export function useClientQueries<Schema extends SchemaDef, Options extends Query
263331 schema : Schema ,
264332 options ?: QueryContext ,
265333) : ClientHooks < Schema , Options > {
266- return Object . keys ( schema . models ) . reduce (
334+ const result = Object . keys ( schema . models ) . reduce (
267335 ( acc , model ) => {
268336 ( acc as any ) [ lowerCaseFirst ( model ) ] = useModelQueries < Schema , GetModels < Schema > , Options > (
269337 schema ,
@@ -274,6 +342,137 @@ export function useClientQueries<Schema extends SchemaDef, Options extends Query
274342 } ,
275343 { } as ClientHooks < Schema , Options > ,
276344 ) ;
345+
346+ const procedures = ( schema as any ) . procedures as Record < string , { mutation ?: boolean } > | undefined ;
347+ if ( procedures ) {
348+ const buildProcedureHooks = ( endpointModel : '$procs' | '$procedures' ) => {
349+ return Object . keys ( procedures ) . reduce ( ( acc , name ) => {
350+ const procDef = procedures [ name ] ;
351+ if ( procDef ?. mutation ) {
352+ acc [ name ] = {
353+ useMutation : ( hookOptions ?: any ) =>
354+ useInternalProcedureMutation ( schema , endpointModel , name , { ...options , ...hookOptions } ) ,
355+ } ;
356+ } else {
357+ acc [ name ] = {
358+ useQuery : ( args ?: any , hookOptions ?: any ) =>
359+ useInternalProcedureQuery ( schema , endpointModel , name , args , { ...options , ...hookOptions } ) ,
360+ useSuspenseQuery : ( args ?: any , hookOptions ?: any ) =>
361+ useInternalProcedureSuspenseQuery ( schema , endpointModel , name , args , {
362+ ...options ,
363+ ...hookOptions ,
364+ } ) ,
365+ useInfiniteQuery : ( args ?: any , hookOptions ?: any ) =>
366+ useInternalProcedureInfiniteQuery ( schema , endpointModel , name , args , {
367+ ...options ,
368+ ...hookOptions ,
369+ } ) ,
370+ useSuspenseInfiniteQuery : ( args ?: any , hookOptions ?: any ) =>
371+ useInternalProcedureSuspenseInfiniteQuery ( schema , endpointModel , name , args , {
372+ ...options ,
373+ ...hookOptions ,
374+ } ) ,
375+ } ;
376+ }
377+ return acc ;
378+ } , { } as any ) ;
379+ } ;
380+
381+ ( result as any ) . $procs = buildProcedureHooks ( '$procs' ) ;
382+ ( result as any ) . $procedures = buildProcedureHooks ( '$procedures' ) ;
383+ }
384+
385+ return result ;
386+ }
387+
388+ export function useInternalProcedureQuery < TQueryFnData , TData > (
389+ _schema : SchemaDef ,
390+ endpointModel : '$procs' | '$procedures' ,
391+ procedure : string ,
392+ args ?: unknown ,
393+ options ?: Omit < UseQueryOptions < TQueryFnData , DefaultError , TData > , 'queryKey' > & ExtraQueryOptions ,
394+ ) {
395+ const { endpoint, fetch } = useFetchOptions ( options ) ;
396+ const reqUrl = makeUrl ( endpoint , endpointModel , procedure , args ) ;
397+ const queryKey = getQueryKey ( endpointModel , procedure , args , {
398+ infinite : false ,
399+ optimisticUpdate : false ,
400+ } ) ;
401+ return {
402+ queryKey,
403+ ...useQuery ( {
404+ queryKey,
405+ queryFn : ( { signal } ) => fetcher < TQueryFnData > ( reqUrl , { signal } , fetch ) ,
406+ ...options ,
407+ } ) ,
408+ } ;
409+ }
410+
411+ export function useInternalProcedureSuspenseQuery < TQueryFnData , TData > (
412+ _schema : SchemaDef ,
413+ endpointModel : '$procs' | '$procedures' ,
414+ procedure : string ,
415+ args ?: unknown ,
416+ options ?: Omit < UseSuspenseQueryOptions < TQueryFnData , DefaultError , TData > , 'queryKey' > & ExtraQueryOptions ,
417+ ) {
418+ const { endpoint, fetch } = useFetchOptions ( options ) ;
419+ const reqUrl = makeUrl ( endpoint , endpointModel , procedure , args ) ;
420+ const queryKey = getQueryKey ( endpointModel , procedure , args , {
421+ infinite : false ,
422+ optimisticUpdate : false ,
423+ } ) ;
424+ return {
425+ queryKey,
426+ ...useSuspenseQuery ( {
427+ queryKey,
428+ queryFn : ( { signal } ) => fetcher < TQueryFnData > ( reqUrl , { signal } , fetch ) ,
429+ ...options ,
430+ } ) ,
431+ } ;
432+ }
433+
434+ export function useInternalProcedureInfiniteQuery < TQueryFnData , TData > (
435+ schema : SchemaDef ,
436+ endpointModel : '$procs' | '$procedures' ,
437+ procedure : string ,
438+ args : unknown ,
439+ options :
440+ | ( Omit <
441+ UseInfiniteQueryOptions < TQueryFnData , DefaultError , InfiniteData < TData > > ,
442+ 'queryKey' | 'initialPageParam'
443+ > &
444+ QueryContext )
445+ | undefined ,
446+ ) {
447+ return useInternalInfiniteQuery ( schema , endpointModel , procedure , args , options ) ;
448+ }
449+
450+ export function useInternalProcedureSuspenseInfiniteQuery < TQueryFnData , TData > (
451+ schema : SchemaDef ,
452+ endpointModel : '$procs' | '$procedures' ,
453+ procedure : string ,
454+ args : unknown ,
455+ options : Omit <
456+ UseSuspenseInfiniteQueryOptions < TQueryFnData , DefaultError , InfiniteData < TData > > & QueryContext ,
457+ 'queryKey' | 'initialPageParam'
458+ > ,
459+ ) {
460+ return useInternalSuspenseInfiniteQuery ( schema , endpointModel , procedure , args , options ) ;
461+ }
462+
463+ export function useInternalProcedureMutation < TArgs , R = any > (
464+ _schema : SchemaDef ,
465+ endpointModel : '$procs' | '$procedures' ,
466+ procedure : string ,
467+ options ?: Omit < UseMutationOptions < R , DefaultError , TArgs > , 'mutationFn' > & QueryContext ,
468+ ) {
469+ const { endpoint, fetch } = useFetchOptions ( options ) ;
470+ const mutationFn = ( data : any ) => {
471+ const reqUrl = makeUrl ( endpoint , endpointModel , procedure , data ) ;
472+ return fetcher < R > ( reqUrl , { method : 'POST' } , fetch ) as Promise < R > ;
473+ } ;
474+
475+ return useMutation ( { ...options , mutationFn } ) ;
277476}
278477
279478/**
0 commit comments