Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 2c9db43

Browse files
ymc9DoctorFTB
andauthored
feat(cli): implement watch mode for generate (#573)
* feat(cli): implement watch mode for generate * chore(root): update pnpm-lock.yaml * chore(cli): track all model declaration and removed paths, logs in past tense * fix(cli): typo, unused double array from * fix(orm): preserve zod validation errors when validating custom json types * update * chore(cli): move import, fix parallel generation on watch * feat(common-helpers): implement single-debounce * chore(cli): use single-debounce for debouncing * feat(common-helpers): implement single-debounce * fix(common-helpers): re run single-debounce * fix(tanstack): avoid invalidating queries for custom proc mutations * add missing file * fix formatting --------- Co-authored-by: FTB_lag <tabolskyy.git@gmail.com>
1 parent 2172614 commit 2c9db43

41 files changed

Lines changed: 1545 additions & 850 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const CUSTOM_PROC_ROUTE_NAME = '$procs';

packages/clients/tanstack-query/src/react.ts

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import type {
5555
import type { GetModels, SchemaDef } from '@zenstackhq/schema';
5656
import { createContext, useContext } from 'react';
5757
import { getAllQueries, invalidateQueriesMatchingPredicate } from './common/client';
58+
import { CUSTOM_PROC_ROUTE_NAME } from './common/constants';
5859
import { getQueryKey } from './common/query-key';
5960
import type {
6061
ExtraMutationOptions,
@@ -350,30 +351,36 @@ export function useClientQueries<Schema extends SchemaDef, Options extends Query
350351

351352
const procedures = (schema as any).procedures as Record<string, { mutation?: boolean }> | undefined;
352353
if (procedures) {
353-
const buildProcedureHooks = (endpointModel: '$procs') => {
354+
const buildProcedureHooks = () => {
354355
return Object.keys(procedures).reduce((acc, name) => {
355356
const procDef = procedures[name];
356357
if (procDef?.mutation) {
357358
acc[name] = {
358359
useMutation: (hookOptions?: any) =>
359-
useInternalMutation(schema, endpointModel, 'POST', name, { ...options, ...hookOptions }),
360+
useInternalMutation(schema, CUSTOM_PROC_ROUTE_NAME, 'POST', name, {
361+
...options,
362+
...hookOptions,
363+
}),
360364
};
361365
} else {
362366
acc[name] = {
363367
useQuery: (args?: any, hookOptions?: any) =>
364-
useInternalQuery(schema, endpointModel, name, args, { ...options, ...hookOptions }),
368+
useInternalQuery(schema, CUSTOM_PROC_ROUTE_NAME, name, args, {
369+
...options,
370+
...hookOptions,
371+
}),
365372
useSuspenseQuery: (args?: any, hookOptions?: any) =>
366-
useInternalSuspenseQuery(schema, endpointModel, name, args, {
373+
useInternalSuspenseQuery(schema, CUSTOM_PROC_ROUTE_NAME, name, args, {
367374
...options,
368375
...hookOptions,
369376
}),
370377
useInfiniteQuery: (args?: any, hookOptions?: any) =>
371-
useInternalInfiniteQuery(schema, endpointModel, name, args, {
378+
useInternalInfiniteQuery(schema, CUSTOM_PROC_ROUTE_NAME, name, args, {
372379
...options,
373380
...hookOptions,
374381
}),
375382
useSuspenseInfiniteQuery: (args?: any, hookOptions?: any) =>
376-
useInternalSuspenseInfiniteQuery(schema, endpointModel, name, args, {
383+
useInternalSuspenseInfiniteQuery(schema, CUSTOM_PROC_ROUTE_NAME, name, args, {
377384
...options,
378385
...hookOptions,
379386
}),
@@ -383,7 +390,7 @@ export function useClientQueries<Schema extends SchemaDef, Options extends Query
383390
}, {} as any);
384391
};
385392

386-
(result as any).$procs = buildProcedureHooks('$procs');
393+
(result as any).$procs = buildProcedureHooks();
387394
}
388395

389396
return result;
@@ -645,64 +652,68 @@ export function useInternalMutation<TArgs, R = any>(
645652
};
646653

647654
const finalOptions = { ...options, mutationFn };
648-
const invalidateQueries = options?.invalidateQueries !== false;
649-
const optimisticUpdate = !!options?.optimisticUpdate;
650-
651-
if (!optimisticUpdate) {
652-
// if optimistic update is not enabled, invalidate related queries on success
653-
if (invalidateQueries) {
654-
const invalidator = createInvalidator(
655+
if (model !== CUSTOM_PROC_ROUTE_NAME) {
656+
// not a custom procedure, set up optimistic update and invalidation
657+
658+
const invalidateQueries = options?.invalidateQueries !== false;
659+
const optimisticUpdate = !!options?.optimisticUpdate;
660+
661+
if (!optimisticUpdate) {
662+
// if optimistic update is not enabled, invalidate related queries on success
663+
if (invalidateQueries) {
664+
const invalidator = createInvalidator(
665+
model,
666+
operation,
667+
schema,
668+
(predicate) => invalidateQueriesMatchingPredicate(queryClient, predicate),
669+
logging,
670+
);
671+
const origOnSuccess = finalOptions.onSuccess;
672+
finalOptions.onSuccess = async (...args) => {
673+
// execute invalidator prior to user-provided onSuccess
674+
await invalidator(...args);
675+
676+
// call user-provided onSuccess
677+
await origOnSuccess?.(...args);
678+
};
679+
}
680+
} else {
681+
// schedule optimistic update on mutate
682+
const optimisticUpdater = createOptimisticUpdater(
655683
model,
656684
operation,
657685
schema,
658-
(predicate) => invalidateQueriesMatchingPredicate(queryClient, predicate),
686+
{ optimisticDataProvider: finalOptions.optimisticDataProvider },
687+
() => getAllQueries(queryClient),
659688
logging,
660689
);
661-
const origOnSuccess = finalOptions.onSuccess;
662-
finalOptions.onSuccess = async (...args) => {
663-
// execute invalidator prior to user-provided onSuccess
664-
await invalidator(...args);
690+
const origOnMutate = finalOptions.onMutate;
691+
finalOptions.onMutate = async (...args) => {
692+
// execute optimistic update
693+
await optimisticUpdater(...args);
665694

666-
// call user-provided onSuccess
667-
await origOnSuccess?.(...args);
695+
// call user-provided onMutate
696+
return origOnMutate?.(...args);
668697
};
669-
}
670-
} else {
671-
// schedule optimistic update on mutate
672-
const optimisticUpdater = createOptimisticUpdater(
673-
model,
674-
operation,
675-
schema,
676-
{ optimisticDataProvider: finalOptions.optimisticDataProvider },
677-
() => getAllQueries(queryClient),
678-
logging,
679-
);
680-
const origOnMutate = finalOptions.onMutate;
681-
finalOptions.onMutate = async (...args) => {
682-
// execute optimistic update
683-
await optimisticUpdater(...args);
684-
685-
// call user-provided onMutate
686-
return origOnMutate?.(...args);
687-
};
688-
689-
if (invalidateQueries) {
690-
// invalidate related queries on settled (success or error)
691-
const invalidator = createInvalidator(
692-
model,
693-
operation,
694-
schema,
695-
(predicate) => invalidateQueriesMatchingPredicate(queryClient, predicate),
696-
logging,
697-
);
698-
const origOnSettled = finalOptions.onSettled;
699-
finalOptions.onSettled = async (...args) => {
700-
// execute invalidator prior to user-provided onSettled
701-
await invalidator(...args);
702698

703-
// call user-provided onSettled
704-
return origOnSettled?.(...args);
705-
};
699+
if (invalidateQueries) {
700+
// invalidate related queries on settled (success or error)
701+
const invalidator = createInvalidator(
702+
model,
703+
operation,
704+
schema,
705+
(predicate) => invalidateQueriesMatchingPredicate(queryClient, predicate),
706+
logging,
707+
);
708+
const origOnSettled = finalOptions.onSettled;
709+
finalOptions.onSettled = async (...args) => {
710+
// execute invalidator prior to user-provided onSettled
711+
await invalidator(...args);
712+
713+
// call user-provided onSettled
714+
return origOnSettled?.(...args);
715+
};
716+
}
706717
}
707718
}
708719

packages/clients/tanstack-query/src/svelte/index.svelte.ts

Lines changed: 77 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import type {
5656
import type { GetModels, SchemaDef } from '@zenstackhq/schema';
5757
import { getContext, setContext } from 'svelte';
5858
import { getAllQueries, invalidateQueriesMatchingPredicate } from '../common/client';
59+
import { CUSTOM_PROC_ROUTE_NAME } from '../common/constants';
5960
import { getQueryKey } from '../common/query-key';
6061
import type {
6162
ExtraMutationOptions,
@@ -297,27 +298,39 @@ export function useClientQueries<Schema extends SchemaDef, Options extends Query
297298

298299
const procedures = (schema as any).procedures as Record<string, { mutation?: boolean }> | undefined;
299300
if (procedures) {
300-
const buildProcedureHooks = (endpointModel: '$procs') => {
301+
const buildProcedureHooks = () => {
301302
return Object.keys(procedures).reduce((acc, name) => {
302303
const procDef = procedures[name];
303304
if (procDef?.mutation) {
304305
acc[name] = {
305306
useMutation: (hookOptions?: any) =>
306-
useInternalMutation(schema, endpointModel, 'POST', name, merge(options, hookOptions)),
307+
useInternalMutation(
308+
schema,
309+
CUSTOM_PROC_ROUTE_NAME,
310+
'POST',
311+
name,
312+
merge(options, hookOptions),
313+
),
307314
};
308315
} else {
309316
acc[name] = {
310317
useQuery: (args?: any, hookOptions?: any) =>
311-
useInternalQuery(schema, endpointModel, name, args, merge(options, hookOptions)),
318+
useInternalQuery(schema, CUSTOM_PROC_ROUTE_NAME, name, args, merge(options, hookOptions)),
312319
useInfiniteQuery: (args?: any, hookOptions?: any) =>
313-
useInternalInfiniteQuery(schema, endpointModel, name, args, merge(options, hookOptions)),
320+
useInternalInfiniteQuery(
321+
schema,
322+
CUSTOM_PROC_ROUTE_NAME,
323+
name,
324+
args,
325+
merge(options, hookOptions),
326+
),
314327
};
315328
}
316329
return acc;
317330
}, {} as any);
318331
};
319332

320-
(result as any).$procs = buildProcedureHooks('$procs');
333+
(result as any).$procs = buildProcedureHooks();
321334
}
322335

323336
return result;
@@ -533,70 +546,74 @@ export function useInternalMutation<TArgs, R = any>(
533546
mutationFn,
534547
};
535548

536-
if (!optimisticUpdate) {
537-
// if optimistic update is not enabled, invalidate related queries on success
538-
if (invalidateQueries) {
539-
const invalidator = createInvalidator(
549+
if (model !== CUSTOM_PROC_ROUTE_NAME) {
550+
// not a custom procedure, set up optimistic update and invalidation
551+
552+
if (!optimisticUpdate) {
553+
// if optimistic update is not enabled, invalidate related queries on success
554+
if (invalidateQueries) {
555+
const invalidator = createInvalidator(
556+
model,
557+
operation,
558+
schema,
559+
(predicate: InvalidationPredicate) =>
560+
// @ts-ignore
561+
invalidateQueriesMatchingPredicate(queryClient, predicate),
562+
logging,
563+
);
564+
565+
// execute invalidator prior to user-provided onSuccess
566+
const origOnSuccess = optionsValue?.onSuccess;
567+
const wrappedOnSuccess: typeof origOnSuccess = async (...args) => {
568+
await invalidator(...args);
569+
await origOnSuccess?.(...args);
570+
};
571+
result.onSuccess = wrappedOnSuccess;
572+
}
573+
} else {
574+
const optimisticUpdater = createOptimisticUpdater(
540575
model,
541576
operation,
542577
schema,
543-
(predicate: InvalidationPredicate) =>
544-
// @ts-ignore
545-
invalidateQueriesMatchingPredicate(queryClient, predicate),
578+
{ optimisticDataProvider: optionsValue?.optimisticDataProvider },
579+
// @ts-ignore
580+
() => getAllQueries(queryClient),
546581
logging,
547582
);
548583

549-
// execute invalidator prior to user-provided onSuccess
550-
const origOnSuccess = optionsValue?.onSuccess;
551-
const wrappedOnSuccess: typeof origOnSuccess = async (...args) => {
552-
await invalidator(...args);
553-
await origOnSuccess?.(...args);
554-
};
555-
result.onSuccess = wrappedOnSuccess;
556-
}
557-
} else {
558-
const optimisticUpdater = createOptimisticUpdater(
559-
model,
560-
operation,
561-
schema,
562-
{ optimisticDataProvider: optionsValue?.optimisticDataProvider },
563-
// @ts-ignore
564-
() => getAllQueries(queryClient),
565-
logging,
566-
);
567-
568-
const origOnMutate = optionsValue.onMutate;
569-
const wrappedOnMutate: typeof origOnMutate = async (...args) => {
570-
// execute optimistic updater prior to user-provided onMutate
571-
await optimisticUpdater(...args);
572-
573-
// call user-provided onMutate
574-
return origOnMutate?.(...args);
575-
};
576-
577-
result.onMutate = wrappedOnMutate;
584+
const origOnMutate = optionsValue.onMutate;
585+
const wrappedOnMutate: typeof origOnMutate = async (...args) => {
586+
// execute optimistic updater prior to user-provided onMutate
587+
await optimisticUpdater(...args);
578588

579-
if (invalidateQueries) {
580-
const invalidator = createInvalidator(
581-
model,
582-
operation,
583-
schema,
584-
(predicate: InvalidationPredicate) =>
585-
// @ts-ignore
586-
invalidateQueriesMatchingPredicate(queryClient, predicate),
587-
logging,
588-
);
589-
const origOnSettled = optionsValue.onSettled;
590-
const wrappedOnSettled: typeof origOnSettled = async (...args) => {
591-
// execute invalidator prior to user-provided onSettled
592-
await invalidator(...args);
593-
594-
// call user-provided onSettled
595-
await origOnSettled?.(...args);
589+
// call user-provided onMutate
590+
return origOnMutate?.(...args);
596591
};
597592

598-
// replace onSettled in mergedOpt
599-
result.onSettled = wrappedOnSettled;
593+
result.onMutate = wrappedOnMutate;
594+
595+
if (invalidateQueries) {
596+
const invalidator = createInvalidator(
597+
model,
598+
operation,
599+
schema,
600+
(predicate: InvalidationPredicate) =>
601+
// @ts-ignore
602+
invalidateQueriesMatchingPredicate(queryClient, predicate),
603+
logging,
604+
);
605+
const origOnSettled = optionsValue.onSettled;
606+
const wrappedOnSettled: typeof origOnSettled = async (...args) => {
607+
// execute invalidator prior to user-provided onSettled
608+
await invalidator(...args);
609+
610+
// call user-provided onSettled
611+
await origOnSettled?.(...args);
612+
};
613+
614+
// replace onSettled in mergedOpt
615+
result.onSettled = wrappedOnSettled;
616+
}
600617
}
601618
}
602619

0 commit comments

Comments
 (0)