diff --git a/packages/plugins/tanstack-query/src/generator.ts b/packages/plugins/tanstack-query/src/generator.ts index 10852e826..3dd040f71 100644 --- a/packages/plugins/tanstack-query/src/generator.ts +++ b/packages/plugins/tanstack-query/src/generator.ts @@ -216,7 +216,7 @@ function generateMutationHook( { name: `_mutation`, initializer: ` - useModelMutation<${argsType}, ${ + useModelMutation<${argsType}, DefaultError, ${ overrideReturnType ?? model }, ${checkReadBack}>('${model}', '${httpVerb.toUpperCase()}', \`\${endpoint}/${lowerCaseFirst( model @@ -565,9 +565,9 @@ function makeBaseImports(target: TargetFramework, version: TanStackVersion) { const runtimeImportBase = makeRuntimeImportBase(version); const shared = [ `import { useModelQuery, useInfiniteModelQuery, useModelMutation } from '${runtimeImportBase}/${target}';`, - `import type { PickEnumerable, CheckSelect } from '${runtimeImportBase}';`, + `import type { PickEnumerable, CheckSelect, QueryError } from '${runtimeImportBase}';`, `import metadata from './__model_meta';`, - `type DefaultError = Error;`, + `type DefaultError = QueryError;`, ]; switch (target) { case 'react': { @@ -643,11 +643,11 @@ function makeQueryOptions( function makeMutationOptions(target: string, returnType: string, argsType: string) { switch (target) { case 'react': - return `UseMutationOptions<${returnType}, unknown, ${argsType}>`; + return `UseMutationOptions<${returnType}, DefaultError, ${argsType}>`; case 'vue': - return `UseMutationOptions<${returnType}, unknown, ${argsType}, unknown>`; + return `UseMutationOptions<${returnType}, DefaultError, ${argsType}, unknown>`; case 'svelte': - return `MutationOptions<${returnType}, unknown, ${argsType}>`; + return `MutationOptions<${returnType}, DefaultError, ${argsType}>`; default: throw new PluginError(name, `Unsupported target: ${target}`); } diff --git a/packages/plugins/tanstack-query/src/runtime-v5/index.ts b/packages/plugins/tanstack-query/src/runtime-v5/index.ts index 302b775fc..2954d4683 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/index.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/index.ts @@ -1,2 +1,2 @@ export * from '../runtime/prisma-types'; -export { type FetchFn, getQueryKey } from '../runtime/common'; +export { type FetchFn, type QueryError, getQueryKey } from '../runtime/common'; diff --git a/packages/plugins/tanstack-query/src/runtime-v5/react.ts b/packages/plugins/tanstack-query/src/runtime-v5/react.ts index 375cb2676..92194535f 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/react.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/react.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { + UseSuspenseInfiniteQueryOptions, + UseSuspenseQueryOptions, useInfiniteQuery, useMutation, useQuery, @@ -10,14 +12,11 @@ import { type UseInfiniteQueryOptions, type UseMutationOptions, type UseQueryOptions, - UseSuspenseInfiniteQueryOptions, - UseSuspenseQueryOptions, } from '@tanstack/react-query-v5'; import type { ModelMeta } from '@zenstackhq/runtime/cross'; import { createContext, useContext } from 'react'; import { DEFAULT_QUERY_ENDPOINT, - FetchFn, fetcher, getQueryKey, makeUrl, @@ -25,6 +24,7 @@ import { setupInvalidation, setupOptimisticUpdate, type APIContext, + type FetchFn, } from '../runtime/common'; /** @@ -167,12 +167,18 @@ export function useSuspenseInfiniteModelQuery( * @param checkReadBack Whether to check for read back errors and return undefined if found. * @param optimisticUpdate Whether to enable automatic optimistic update */ -export function useModelMutation( +export function useModelMutation< + TArgs, + TError, + R = any, + C extends boolean = boolean, + Result = C extends true ? R | undefined : R +>( model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, - options?: Omit, 'mutationFn'>, + options?: Omit, 'mutationFn'>, fetch?: FetchFn, invalidateQueries = true, checkReadBack?: C, diff --git a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts index 5f479138e..7de2202d6 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts @@ -16,13 +16,13 @@ import { Readable, derived } from 'svelte/store'; import { APIContext, DEFAULT_QUERY_ENDPOINT, - FetchFn, fetcher, getQueryKey, makeUrl, marshal, setupInvalidation, setupOptimisticUpdate, + type FetchFn, } from '../runtime/common'; export { APIContext as RequestHandlerContext } from '../runtime/common'; @@ -147,12 +147,18 @@ function isStore(opt: unknown): opt is Readable { * @param invalidateQueries Whether to invalidate queries after mutation. * @returns useMutation hooks */ -export function useModelMutation( +export function useModelMutation< + TArgs, + TError, + R = any, + C extends boolean = boolean, + Result = C extends true ? R | undefined : R +>( model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, - options?: Omit, 'mutationFn'>, + options?: Omit, 'mutationFn'>, fetch?: FetchFn, invalidateQueries = true, checkReadBack?: C, diff --git a/packages/plugins/tanstack-query/src/runtime/common.ts b/packages/plugins/tanstack-query/src/runtime/common.ts index ea64bd3e2..b0d45b246 100644 --- a/packages/plugins/tanstack-query/src/runtime/common.ts +++ b/packages/plugins/tanstack-query/src/runtime/common.ts @@ -25,6 +25,21 @@ export const QUERY_KEY_PREFIX = 'zenstack'; */ export type FetchFn = (url: string, options?: RequestInit) => Promise; +/** + * Type for query and mutation errors. + */ +export type QueryError = Error & { + /** + * Additional error information. + */ + info?: unknown; + + /** + * HTTP status code. + */ + status?: number; +}; + /** * Context type for configuring the hooks. */ @@ -64,9 +79,7 @@ export async function fetcher( // policy doesn't allow mutation result to be read back, just return undefined return undefined as any; } - const error: Error & { info?: unknown; status?: number } = new Error( - 'An error occurred while fetching the data.' - ); + const error: QueryError = new Error('An error occurred while fetching the data.'); error.info = errData.error; error.status = res.status; throw error; diff --git a/packages/plugins/tanstack-query/src/runtime/index.ts b/packages/plugins/tanstack-query/src/runtime/index.ts index 909c0c4bf..0894bc461 100644 --- a/packages/plugins/tanstack-query/src/runtime/index.ts +++ b/packages/plugins/tanstack-query/src/runtime/index.ts @@ -1,2 +1,2 @@ export * from './prisma-types'; -export { type FetchFn, getQueryKey } from './common'; +export { type FetchFn, type QueryError, getQueryKey } from './common'; diff --git a/packages/plugins/tanstack-query/src/runtime/react.ts b/packages/plugins/tanstack-query/src/runtime/react.ts index 2f75d88eb..607b57430 100644 --- a/packages/plugins/tanstack-query/src/runtime/react.ts +++ b/packages/plugins/tanstack-query/src/runtime/react.ts @@ -12,7 +12,6 @@ import type { ModelMeta } from '@zenstackhq/runtime/cross'; import { createContext, useContext } from 'react'; import { DEFAULT_QUERY_ENDPOINT, - FetchFn, fetcher, getQueryKey, makeUrl, @@ -20,6 +19,7 @@ import { setupInvalidation, setupOptimisticUpdate, type APIContext, + type FetchFn, } from './common'; /** @@ -110,12 +110,18 @@ export function useInfiniteModelQuery( * @param optimisticUpdate Whether to enable automatic optimistic update * @returns useMutation hooks */ -export function useModelMutation( +export function useModelMutation< + TArgs, + TError, + R = any, + C extends boolean = boolean, + Result = C extends true ? R | undefined : R +>( model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, - options?: Omit, 'mutationFn'>, + options?: Omit, 'mutationFn'>, fetch?: FetchFn, invalidateQueries = true, checkReadBack?: C, diff --git a/packages/plugins/tanstack-query/src/runtime/svelte.ts b/packages/plugins/tanstack-query/src/runtime/svelte.ts index 88c675a82..dbd0342aa 100644 --- a/packages/plugins/tanstack-query/src/runtime/svelte.ts +++ b/packages/plugins/tanstack-query/src/runtime/svelte.ts @@ -13,13 +13,13 @@ import { getContext, setContext } from 'svelte'; import { APIContext, DEFAULT_QUERY_ENDPOINT, - FetchFn, fetcher, getQueryKey, makeUrl, marshal, setupInvalidation, setupOptimisticUpdate, + type FetchFn, } from './common'; export { APIContext as RequestHandlerContext } from './common'; @@ -109,12 +109,18 @@ export function useInfiniteModelQuery( * @param optimisticUpdate Whether to enable automatic optimistic update. * @returns useMutation hooks */ -export function useModelMutation( +export function useModelMutation< + TArgs, + TError, + R = any, + C extends boolean = boolean, + Result = C extends true ? R | undefined : R +>( model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, - options?: Omit, 'mutationFn'>, + options?: Omit, 'mutationFn'>, fetch?: FetchFn, invalidateQueries = true, checkReadBack?: C, diff --git a/packages/plugins/tanstack-query/src/runtime/vue.ts b/packages/plugins/tanstack-query/src/runtime/vue.ts index b0a35f5f3..049b66907 100644 --- a/packages/plugins/tanstack-query/src/runtime/vue.ts +++ b/packages/plugins/tanstack-query/src/runtime/vue.ts @@ -14,13 +14,13 @@ import { inject, provide } from 'vue'; import { APIContext, DEFAULT_QUERY_ENDPOINT, - FetchFn, fetcher, getQueryKey, makeUrl, marshal, setupInvalidation, setupOptimisticUpdate, + type FetchFn, } from './common'; export { APIContext as RequestHandlerContext } from './common'; @@ -113,12 +113,18 @@ export function useInfiniteModelQuery( * @param optimisticUpdate Whether to enable automatic optimistic update * @returns useMutation hooks */ -export function useModelMutation( +export function useModelMutation< + TArgs, + TError, + R = any, + C extends boolean = boolean, + Result = C extends true ? R | undefined : R +>( model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, - options?: Omit, 'mutationFn'>, + options?: Omit, 'mutationFn'>, fetch?: FetchFn, invalidateQueries = true, checkReadBack?: C, @@ -168,5 +174,5 @@ export function useModelMutation(finalOptions); + return useMutation(finalOptions); } diff --git a/packages/plugins/trpc/tests/projects/t3-trpc-v10/package.json b/packages/plugins/trpc/tests/projects/t3-trpc-v10/package.json index 5878a8037..a9c99ebba 100644 --- a/packages/plugins/trpc/tests/projects/t3-trpc-v10/package.json +++ b/packages/plugins/trpc/tests/projects/t3-trpc-v10/package.json @@ -20,14 +20,10 @@ "@trpc/next": "^10.43.6", "@trpc/react-query": "^10.43.6", "@trpc/server": "^10.43.6", - "@zenstackhq/language": "file:../../../../../../.build/zenstackhq-language-2.0.0-alpha.2.tgz", - "@zenstackhq/runtime": "file:../../../../../../.build/zenstackhq-runtime-2.0.0-alpha.2.tgz", - "@zenstackhq/sdk": "file:../../../../../../.build/zenstackhq-sdk-2.0.0-alpha.2.tgz", "next": "^14.0.4", "react": "18.2.0", "react-dom": "18.2.0", "superjson": "^2.2.1", - "zenstack": "file:../../../../../../.build/zenstack-2.0.0-alpha.2.tgz", "zod": "^3.22.4" }, "devDependencies": {