From ec8a30490a57fa6561a1ed698cdff2f7894f5d41 Mon Sep 17 00:00:00 2001 From: Philipp Aleksovski Date: Mon, 4 Aug 2025 11:51:38 +0200 Subject: [PATCH 1/5] Finished-plugin --- packages/plugins/tanstack-query/package.json | 18 +- .../tanstack-query/scripts/postbuild.js | 7 + .../plugins/tanstack-query/src/generator.ts | 50 ++++- .../tanstack-query/src/runtime-v5/angular.ts | 202 ++++++++++++++++++ .../tanstack-query/tests/plugin.test.ts | 66 ++++++ .../plugins/tanstack-query/tsup-v5.config.ts | 2 +- pnpm-lock.yaml | 114 +++++++++- 7 files changed, 451 insertions(+), 8 deletions(-) create mode 100644 packages/plugins/tanstack-query/src/runtime-v5/angular.ts diff --git a/packages/plugins/tanstack-query/package.json b/packages/plugins/tanstack-query/package.json index 8793764b9..4a481470c 100644 --- a/packages/plugins/tanstack-query/package.json +++ b/packages/plugins/tanstack-query/package.json @@ -58,6 +58,12 @@ "import": "./runtime-v5/svelte.mjs", "require": "./runtime-v5/svelte.js", "default": "./runtime-v5/svelte.js" + }, + "./runtime-v5/angular": { + "types": "./runtime-v5/angular.d.ts", + "import": "./runtime-v5/angular.mjs", + "require": "./runtime-v5/angular.js", + "default": "./runtime-v5/angular.js" } }, "repository": { @@ -87,7 +93,14 @@ "ts-morph": "^16.0.0", "ts-pattern": "^4.3.0" }, + "peerDependencies": { + "@tanstack/angular-query-experimental": "^5.0.0" + }, "devDependencies": { + "@angular/core": "^20.0.0", + "@angular/common": "^20.0.0", + "@angular/platform-browser": "^20.0.0", + "@tanstack/angular-query-v5": "npm:@tanstack/angular-query-experimental@5.84.x", "@tanstack/react-query": "^4.29.7", "@tanstack/react-query-v5": "npm:@tanstack/react-query@5.56.x", "@tanstack/svelte-query": "^4.29.7", @@ -103,9 +116,12 @@ "nock": "^13.3.6", "react": "18.2.0", "react-test-renderer": "^18.2.0", + "rxjs": "^7.8.0", "svelte": "^4.2.1", "swr": "^2.0.3", "tmp": "^0.2.3", - "vue": "^3.3.4" + "typescript": "^5.0.0", + "vue": "^3.3.4", + "zone.js": "^0.15.0" } } diff --git a/packages/plugins/tanstack-query/scripts/postbuild.js b/packages/plugins/tanstack-query/scripts/postbuild.js index 9fbbace04..02abe2dfb 100755 --- a/packages/plugins/tanstack-query/scripts/postbuild.js +++ b/packages/plugins/tanstack-query/scripts/postbuild.js @@ -45,3 +45,10 @@ replaceSync({ from: /@tanstack\/vue-query-v5/g, to: '@tanstack/vue-query', }); + +console.log('Replacing @tanstack/angular-query-v5'); +replaceSync({ + file: 'dist/runtime-v5/angular*(.d.ts|.d.mts|.js|.mjs)', + from: /@tanstack\/angular-query-v5/g, + to: '@tanstack/angular-query-experimental', +}); diff --git a/packages/plugins/tanstack-query/src/generator.ts b/packages/plugins/tanstack-query/src/generator.ts index 31e5c235a..faae74f10 100644 --- a/packages/plugins/tanstack-query/src/generator.ts +++ b/packages/plugins/tanstack-query/src/generator.ts @@ -21,7 +21,7 @@ import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph'; import { P, match } from 'ts-pattern'; import { name } from '.'; -const supportedTargets = ['react', 'vue', 'svelte']; +const supportedTargets = ['react', 'vue', 'svelte', 'angular']; type TargetFramework = (typeof supportedTargets)[number]; type TanStackVersion = 'v4' | 'v5'; @@ -41,6 +41,11 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF. throw new PluginError(name, `Unsupported version "${version}": use "v4" or "v5"`); } + // Angular is only supported in v5 + if (target === 'angular' && version !== 'v5') { + throw new PluginError(name, `Angular target is only supported with version "v5", got "${version}"`); + } + let outDir = requireOption(options, 'output', name); outDir = resolvePath(outDir, options); ensureEmptyDir(outDir); @@ -273,6 +278,31 @@ function generateMutationHook( }); break; + case 'angular': + // Angular uses injectMutation which returns the mutation object directly + // override the mutateAsync function to return the correct type + func.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'mutation', + initializer: `{ + ..._mutation, + mutateAsync: async ( + args: Prisma.SelectSubset, + options?: ${optionsType} + ) => { + return (await _mutation.mutateAsync( + args, + options as any + )) as ${returnType}; + }, + }`, + }, + ], + }); + break; + default: throw new PluginError(name, `Unsupported target "${target}"`); } @@ -597,6 +627,9 @@ function generateIndex( case 'svelte': sf.addStatements(`export { SvelteQueryContextKey, setHooksContext } from '${runtimeImportBase}/svelte';`); break; + case 'angular': + sf.addStatements(`export { AngularQueryContextKey, provideAngularQueryContext } from '${runtimeImportBase}/angular';`); + break; } sf.addStatements(`export { default as metadata } from './__model_meta';`); } @@ -609,6 +642,8 @@ function makeGetContext(target: TargetFramework) { return 'const { endpoint, fetch } = getHooksContext();'; case 'svelte': return `const { endpoint, fetch } = getHooksContext();`; + case 'angular': + return 'const { endpoint, fetch } = getHooksContext();'; default: throw new PluginError(name, `Unsupported target "${target}"`); } @@ -658,6 +693,13 @@ function makeBaseImports(target: TargetFramework, version: TanStackVersion) { ...shared, ]; } + case 'angular': { + return [ + `import type { CreateMutationOptions, CreateQueryOptions, CreateInfiniteQueryOptions, InfiniteData } from '@tanstack/angular-query-v5';`, + `import { getHooksContext } from '${runtimeImportBase}/${target}';`, + ...shared, + ]; + } default: throw new PluginError(name, `Unsupported target: ${target}`); } @@ -707,6 +749,11 @@ function makeQueryOptions( ? `Omit, 'queryKey'>` : `StoreOrVal, 'queryKey'>>` ) + .with('angular', () => + infinite + ? `Omit>, 'queryKey' | 'initialPageParam'>` + : `Omit, 'queryKey'>` + ) .otherwise(() => { throw new PluginError(name, `Unsupported target: ${target}`); }); @@ -727,6 +774,7 @@ function makeMutationOptions(target: string, returnType: string, argsType: strin return `MaybeRefOrGetter<${baseOption}> | ComputedRef<${baseOption}>`; }) .with('svelte', () => `MutationOptions<${returnType}, DefaultError, ${argsType}>`) + .with('angular', () => `CreateMutationOptions<${returnType}, DefaultError, ${argsType}>`) .otherwise(() => { throw new PluginError(name, `Unsupported target: ${target}`); }); diff --git a/packages/plugins/tanstack-query/src/runtime-v5/angular.ts b/packages/plugins/tanstack-query/src/runtime-v5/angular.ts new file mode 100644 index 000000000..083f891dd --- /dev/null +++ b/packages/plugins/tanstack-query/src/runtime-v5/angular.ts @@ -0,0 +1,202 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + injectQuery, + injectMutation, + injectInfiniteQuery, + injectQueryClient, + type CreateQueryOptions, + type CreateMutationOptions, + type CreateInfiniteQueryOptions, + type InfiniteData, +} from '@tanstack/angular-query-v5'; +import type { ModelMeta } from '@zenstackhq/runtime/cross'; +import { inject, InjectionToken } from '@angular/core'; +import { + APIContext, + DEFAULT_QUERY_ENDPOINT, + fetcher, + getQueryKey, + makeUrl, + marshal, + setupInvalidation, + setupOptimisticUpdate, + type ExtraMutationOptions, + type ExtraQueryOptions, + type FetchFn, +} from '../runtime/common'; + +export { APIContext as RequestHandlerContext } from '../runtime/common'; + +export const AngularQueryContextKey = new InjectionToken('zenstack-angular-query-context'); + +/** + * Provide context for the generated TanStack Query hooks. + */ +export function provideAngularQueryContext(context: APIContext) { + return { + provide: AngularQueryContextKey, + useValue: context, + }; +} + +/** + * Hooks context. + */ +export function getHooksContext() { + const context = inject(AngularQueryContextKey, { + optional: true + }) || { + endpoint: DEFAULT_QUERY_ENDPOINT, + fetch: undefined, + logging: false, + }; + + const { endpoint, ...rest } = context; + return { endpoint: endpoint ?? DEFAULT_QUERY_ENDPOINT, ...rest }; +} + +/** + * Creates an Angular TanStack Query query. + * + * @param model The name of the model under query. + * @param url The request URL. + * @param args The request args object, URL-encoded and appended as "?q=" parameter + * @param options The Angular query options object + * @param fetch The fetch function to use for sending the HTTP request + * @returns injectQuery hook + */ +export function useModelQuery( + model: string, + url: string, + args?: unknown, + options?: Omit, 'queryKey'> & ExtraQueryOptions, + fetch?: FetchFn +): any { + const reqUrl = makeUrl(url, args); + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: options?.optimisticUpdate !== false, + }); + return { + queryKey, + ...injectQuery(() => ({ + queryKey, + queryFn: ({ signal }) => fetcher(reqUrl, { signal }, fetch, false), + ...options, + })), + }; +} + +/** + * Creates an Angular TanStack Query infinite query. + * + * @param model The name of the model under query. + * @param url The request URL. + * @param args The initial request args object, URL-encoded and appended as "?q=" parameter + * @param options The Angular infinite query options object + * @param fetch The fetch function to use for sending the HTTP request + * @returns injectInfiniteQuery hook + */ +export function useInfiniteModelQuery( + model: string, + url: string, + args: unknown, + options: Omit>, 'queryKey' | 'initialPageParam'>, + fetch?: FetchFn +): any { + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + return { + queryKey, + ...injectInfiniteQuery(() => ({ + queryKey, + queryFn: ({ pageParam, signal }) => { + return fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false); + }, + initialPageParam: args, + ...options, + })), + }; +} + +/** + * Creates an Angular TanStack Query mutation. + * + * @param model The name of the model under mutation. + * @param method The HTTP method. + * @param url The request URL. + * @param modelMeta The model metadata. + * @param options The Angular mutation options. + * @param fetch The fetch function to use for sending the HTTP request + * @param checkReadBack Whether to check for read back errors and return undefined if found. + * @returns injectMutation hook + */ +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'> & ExtraMutationOptions, + fetch?: FetchFn, + checkReadBack?: C +): any { + const queryClient = injectQueryClient(); + const mutationFn = (data: any) => { + const reqUrl = method === 'DELETE' ? makeUrl(url, data) : url; + const fetchInit: RequestInit = { + method, + ...(method !== 'DELETE' && { + headers: { + 'content-type': 'application/json', + }, + body: marshal(data), + }), + }; + return fetcher(reqUrl, fetchInit, fetch, checkReadBack) as Promise; + }; + + const finalOptions = { ...options, mutationFn }; + const operation = url.split('/').pop(); + const invalidateQueries = options?.invalidateQueries !== false; + const optimisticUpdate = !!options?.optimisticUpdate; + + if (operation) { + const { logging } = getHooksContext(); + if (invalidateQueries) { + setupInvalidation( + model, + operation, + modelMeta, + finalOptions, + (predicate) => queryClient.invalidateQueries({ predicate }), + logging + ); + } + + if (optimisticUpdate) { + setupOptimisticUpdate( + model, + operation, + modelMeta, + finalOptions, + queryClient.getQueryCache().getAll(), + (queryKey, data) => { + // update query cache + queryClient.setQueryData(queryKey, data); + // cancel on-flight queries to avoid redundant cache updates, + // the settlement of the current mutation will trigger a new revalidation + queryClient.cancelQueries({ queryKey }, { revert: false, silent: true }); + }, + invalidateQueries ? (predicate) => queryClient.invalidateQueries({ predicate }) : undefined, + logging + ); + } + } + + return injectMutation(() => finalOptions); +} \ No newline at end of file diff --git a/packages/plugins/tanstack-query/tests/plugin.test.ts b/packages/plugins/tanstack-query/tests/plugin.test.ts index 950b0d9a2..7cb57c144 100644 --- a/packages/plugins/tanstack-query/tests/plugin.test.ts +++ b/packages/plugins/tanstack-query/tests/plugin.test.ts @@ -291,6 +291,72 @@ ${sharedModel} ); }); + const angularAppSource = { + name: 'main.ts', + content: ` + import { Component, inject } from '@angular/core'; + import { useFindFirstpost_Item, useInfiniteFindManypost_Item, useCreatepost_Item } from './hooks'; + + @Component({ + selector: 'app-test', + template: '
Test Component
' + }) + export class TestComponent { + query() { + const { data, queryKey } = useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false }); + console.log(queryKey); + console.log(data()?.viewCount); + console.log(data()?.author?.email); + } + + infiniteQuery() { + const { data, queryKey, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); + useInfiniteFindManypost_Item({ where: { published: true } }); + useInfiniteFindManypost_Item(undefined, { enabled: true, getNextPageParam: () => null }); + console.log(queryKey); + console.log(data()?.pages[0][0].published); + console.log(data()?.pageParams[0]); + } + + async mutation() { + const { mutateAsync } = useCreatepost_Item(); + const data = await mutateAsync({ data: { title: 'hello' }, include: { author: true } }); + console.log(data?.viewCount); + console.log(data?.author?.email); + } + } + `, + }; + + it('angular-query run plugin v5', async () => { + await loadSchema( + ` +plugin tanstack { + provider = '${normalizePath(path.resolve(__dirname, '../dist'))}' + output = '$projectRoot/hooks' + target = 'angular' +} + +${sharedModel} + `, + { + provider: 'postgresql', + pushDb: false, + extraDependencies: [ + '@angular/core@^20.0.0', + '@angular/common@^20.0.0', + '@angular/platform-browser@^20.0.0', + '@tanstack/angular-query-v5@npm:@tanstack/angular-query-experimental@5.84.x', + 'rxjs@^7.8.0', + 'zone.js@^0.15.0' + ], + copyDependencies: [path.resolve(__dirname, '../dist')], + compile: true, + extraSourceFiles: [angularAppSource], + } + ); + }); + it('clear output', async () => { const { name: projectDir } = tmp.dirSync(); fs.mkdirSync(path.join(projectDir, 'tanstack'), { recursive: true }); diff --git a/packages/plugins/tanstack-query/tsup-v5.config.ts b/packages/plugins/tanstack-query/tsup-v5.config.ts index d619109b7..145f56a5c 100644 --- a/packages/plugins/tanstack-query/tsup-v5.config.ts +++ b/packages/plugins/tanstack-query/tsup-v5.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ['src/runtime-v5/index.ts', 'src/runtime-v5/react.ts', 'src/runtime-v5/vue.ts', 'src/runtime-v5/svelte.ts'], + entry: ['src/runtime-v5/index.ts', 'src/runtime-v5/react.ts', 'src/runtime-v5/vue.ts', 'src/runtime-v5/svelte.ts', 'src/runtime-v5/angular.ts'], outDir: 'dist/runtime-v5', splitting: false, sourcemap: true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4d35a0cc7..904ae0775 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -225,6 +225,9 @@ importers: packages/plugins/tanstack-query: dependencies: + '@tanstack/angular-query-experimental': + specifier: ^5.0.0 + version: 5.84.1(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1)) '@zenstackhq/runtime': specifier: workspace:* version: link:../../runtime/dist @@ -244,6 +247,18 @@ importers: specifier: ^4.3.0 version: 4.3.0 devDependencies: + '@angular/common': + specifier: ^20.0.0 + version: 20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1) + '@angular/core': + specifier: ^20.0.0 + version: 20.1.4(rxjs@7.8.1)(zone.js@0.15.1) + '@angular/platform-browser': + specifier: ^20.0.0 + version: 20.1.4(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1)) + '@tanstack/angular-query-v5': + specifier: npm:@tanstack/angular-query-experimental@5.84.x + version: '@tanstack/angular-query-experimental@5.84.1(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))' '@tanstack/react-query': specifier: ^4.29.7 version: 4.36.1(react-dom@18.3.1(react@18.2.0))(react@18.2.0) @@ -289,6 +304,9 @@ importers: react-test-renderer: specifier: ^18.2.0 version: 18.3.1(react@18.2.0) + rxjs: + specifier: ^7.8.0 + version: 7.8.1 svelte: specifier: ^4.2.1 version: 4.2.18 @@ -298,9 +316,15 @@ importers: tmp: specifier: ^0.2.3 version: 0.2.3 + typescript: + specifier: ^5.0.0 + version: 5.5.2 vue: specifier: ^3.3.4 version: 3.4.31(typescript@5.5.2) + zone.js: + specifier: ^0.15.0 + version: 0.15.1 publishDirectory: dist packages/plugins/trpc: @@ -803,6 +827,37 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@angular/common@20.1.4': + resolution: {integrity: sha512-AL+HdsY5xL2iM1zZ55ce33U+w2LgPJZQwKvHXJJ/Hpk3rpFNamWtRPmJBeq8Z0dQV1lLTMM+2pUatH6p+5pvEg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@angular/core': 20.1.4 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/core@20.1.4': + resolution: {integrity: sha512-aWDux64a9usuVU2SnF0epqjXAj8JO8jViUzZAJAuFKSCtkeNzqP+Z6DjkqsCKrNvGP7xkX1XhhepUygxgh7/6A==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@angular/compiler': 20.1.4 + rxjs: ^6.5.3 || ^7.4.0 + zone.js: ~0.15.0 + peerDependenciesMeta: + '@angular/compiler': + optional: true + zone.js: + optional: true + + '@angular/platform-browser@20.1.4': + resolution: {integrity: sha512-z86NsGSwm5pXCACdWBbp7SC1Xn+UGvuoRqTsi0dNUXT/3WrP6MvZT3TfNKwM63GLUqFAICSt7uFXS84D72ukvA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@angular/animations': 20.1.4 + '@angular/common': 20.1.4 + '@angular/core': 20.1.4 + peerDependenciesMeta: + '@angular/animations': + optional: true + '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} @@ -2919,6 +2974,12 @@ packages: '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@tanstack/angular-query-experimental@5.84.1': + resolution: {integrity: sha512-YQBCD6J29kyju5xAnomHjPYsz9Q/ARvIUIN0xDkpxuhhc/clSWokgj0hlCLYZP8Pj15sxgDn0Lkc/rH7hfsFlQ==} + peerDependencies: + '@angular/common': '>=16.0.0' + '@angular/core': '>=16.0.0' + '@tanstack/match-sorter-utils@8.15.1': resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==} engines: {node: '>=12'} @@ -2932,6 +2993,12 @@ packages: '@tanstack/query-core@5.56.2': resolution: {integrity: sha512-gor0RI3/R5rVV3gXfddh1MM+hgl0Z4G7tj6Xxpq6p2I03NGPaJ8dITY9Gz05zYYb/EJq9vPas/T4wn9EaDPd4Q==} + '@tanstack/query-core@5.83.1': + resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==} + + '@tanstack/query-devtools@5.84.0': + resolution: {integrity: sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==} + '@tanstack/react-query@4.36.1': resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==} peerDependencies: @@ -4128,7 +4195,7 @@ packages: engines: {node: '>= 14'} concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -4566,7 +4633,7 @@ packages: engines: {node: '>=4'} ee-first@1.1.1: - resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} electron-to-chromium@1.4.814: resolution: {integrity: sha512-GVulpHjFu1Y9ZvikvbArHmAhZXtm3wHlpjTMcXNGKl4IQ4jMQjlnz8yMQYYqdLHKi/jEL2+CBC2akWVCoIGUdw==} @@ -6143,7 +6210,7 @@ packages: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} media-typer@0.3.0: - resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} merge-descriptors@1.0.1: @@ -7590,6 +7657,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions spawn-command@0.0.2-1: resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==} @@ -7763,7 +7831,7 @@ packages: superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net superjson@1.13.3: resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==} @@ -7776,6 +7844,7 @@ packages: supertest@6.3.4: resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} engines: {node: '>=6.4.0'} + deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} @@ -8321,7 +8390,7 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} utils-merge@1.0.1: - resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} uuid@10.0.0: @@ -8832,6 +8901,9 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zone.js@0.15.1: + resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} + snapshots: '@ampproject/remapping@2.3.0': @@ -8839,6 +8911,25 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 + '@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1)': + dependencies: + '@angular/core': 20.1.4(rxjs@7.8.1)(zone.js@0.15.1) + rxjs: 7.8.1 + tslib: 2.6.3 + + '@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1)': + dependencies: + rxjs: 7.8.1 + tslib: 2.6.3 + optionalDependencies: + zone.js: 0.15.1 + + '@angular/platform-browser@20.1.4(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))': + dependencies: + '@angular/common': 20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1) + '@angular/core': 20.1.4(rxjs@7.8.1)(zone.js@0.15.1) + tslib: 2.6.3 + '@antfu/utils@0.7.10': {} '@apidevtools/swagger-methods@3.0.2': {} @@ -11319,6 +11410,13 @@ snapshots: '@swc/counter': 0.1.3 tslib: 2.6.3 + '@tanstack/angular-query-experimental@5.84.1(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))': + dependencies: + '@angular/common': 20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1) + '@angular/core': 20.1.4(rxjs@7.8.1)(zone.js@0.15.1) + '@tanstack/query-core': 5.83.1 + '@tanstack/query-devtools': 5.84.0 + '@tanstack/match-sorter-utils@8.15.1': dependencies: remove-accents: 0.5.0 @@ -11329,6 +11427,10 @@ snapshots: '@tanstack/query-core@5.56.2': {} + '@tanstack/query-core@5.83.1': {} + + '@tanstack/query-devtools@5.84.0': {} + '@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.2.0))(react@18.2.0)': dependencies: '@tanstack/query-core': 4.36.1 @@ -18131,3 +18233,5 @@ snapshots: zod: 3.23.8 zod@3.23.8: {} + + zone.js@0.15.1: {} From d395aa98e6608f0b5bc081c5a4b613f163635ef2 Mon Sep 17 00:00:00 2001 From: Philipp Aleksovski Date: Mon, 4 Aug 2025 12:26:39 +0200 Subject: [PATCH 2/5] fixed Typing --- .../tanstack-query/src/runtime-v5/angular.ts | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/plugins/tanstack-query/src/runtime-v5/angular.ts b/packages/plugins/tanstack-query/src/runtime-v5/angular.ts index 083f891dd..87f0881cd 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/angular.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/angular.ts @@ -3,11 +3,13 @@ import { injectQuery, injectMutation, injectInfiniteQuery, - injectQueryClient, + QueryClient, type CreateQueryOptions, type CreateMutationOptions, type CreateInfiniteQueryOptions, type InfiniteData, + CreateInfiniteQueryResult, + QueryKey, } from '@tanstack/angular-query-v5'; import type { ModelMeta } from '@zenstackhq/runtime/cross'; import { inject, InjectionToken } from '@angular/core'; @@ -43,14 +45,14 @@ export function provideAngularQueryContext(context: APIContext) { * Hooks context. */ export function getHooksContext() { - const context = inject(AngularQueryContextKey, { - optional: true + const context = inject(AngularQueryContextKey, { + optional: true, }) || { endpoint: DEFAULT_QUERY_ENDPOINT, fetch: undefined, logging: false, }; - + const { endpoint, ...rest } = context; return { endpoint: endpoint ?? DEFAULT_QUERY_ENDPOINT, ...rest }; } @@ -71,7 +73,7 @@ export function useModelQuery( args?: unknown, options?: Omit, 'queryKey'> & ExtraQueryOptions, fetch?: FetchFn -): any { +) { const reqUrl = makeUrl(url, args); const queryKey = getQueryKey(model, url, args, { infinite: false, @@ -101,9 +103,12 @@ export function useInfiniteModelQuery( model: string, url: string, args: unknown, - options: Omit>, 'queryKey' | 'initialPageParam'>, + options: Omit< + CreateInfiniteQueryOptions>, + 'queryKey' | 'initialPageParam' + >, fetch?: FetchFn -): any { +): CreateInfiniteQueryResult, TError> & { queryKey: QueryKey } { const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); return { queryKey, @@ -144,9 +149,9 @@ export function useModelMutation< options?: Omit, 'mutationFn'> & ExtraMutationOptions, fetch?: FetchFn, checkReadBack?: C -): any { - const queryClient = injectQueryClient(); - const mutationFn = (data: any) => { +) { + const queryClient = inject(QueryClient); + const mutationFn = (data: unknown) => { const reqUrl = method === 'DELETE' ? makeUrl(url, data) : url; const fetchInit: RequestInit = { method, @@ -199,4 +204,4 @@ export function useModelMutation< } return injectMutation(() => finalOptions); -} \ No newline at end of file +} From 809a817e3f0ccdcce7af941e9e0d66752fb7f0f0 Mon Sep 17 00:00:00 2001 From: Philipp Aleksovski Date: Mon, 4 Aug 2025 12:55:50 +0200 Subject: [PATCH 3/5] removed-deduplication --- .../plugins/tanstack-query/src/generator.ts | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/packages/plugins/tanstack-query/src/generator.ts b/packages/plugins/tanstack-query/src/generator.ts index faae74f10..4f901da6c 100644 --- a/packages/plugins/tanstack-query/src/generator.ts +++ b/packages/plugins/tanstack-query/src/generator.ts @@ -229,6 +229,7 @@ function generateMutationHook( switch (target) { case 'react': case 'vue': + case 'angular': // override the mutateAsync function to return the correct type func.addVariableStatement({ declarationKind: VariableDeclarationKind.Const, @@ -278,31 +279,6 @@ function generateMutationHook( }); break; - case 'angular': - // Angular uses injectMutation which returns the mutation object directly - // override the mutateAsync function to return the correct type - func.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'mutation', - initializer: `{ - ..._mutation, - mutateAsync: async ( - args: Prisma.SelectSubset, - options?: ${optionsType} - ) => { - return (await _mutation.mutateAsync( - args, - options as any - )) as ${returnType}; - }, - }`, - }, - ], - }); - break; - default: throw new PluginError(name, `Unsupported target "${target}"`); } @@ -628,7 +604,9 @@ function generateIndex( sf.addStatements(`export { SvelteQueryContextKey, setHooksContext } from '${runtimeImportBase}/svelte';`); break; case 'angular': - sf.addStatements(`export { AngularQueryContextKey, provideAngularQueryContext } from '${runtimeImportBase}/angular';`); + sf.addStatements( + `export { AngularQueryContextKey, provideAngularQueryContext } from '${runtimeImportBase}/angular';` + ); break; } sf.addStatements(`export { default as metadata } from './__model_meta';`); From 6727c638bea7f49d54103390efc5304f990bfa3f Mon Sep 17 00:00:00 2001 From: Philipp Aleksovski Date: Sat, 9 Aug 2025 13:50:14 +0200 Subject: [PATCH 4/5] Removing-Peer-Dependencie --- packages/plugins/tanstack-query/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/plugins/tanstack-query/package.json b/packages/plugins/tanstack-query/package.json index d6709af9a..fdf337d9b 100644 --- a/packages/plugins/tanstack-query/package.json +++ b/packages/plugins/tanstack-query/package.json @@ -93,9 +93,6 @@ "ts-morph": "catalog:", "ts-pattern": "^4.3.0" }, - "peerDependencies": { - "@tanstack/angular-query-experimental": "^5.0.0" - }, "devDependencies": { "@angular/core": "^20.0.0", "@angular/common": "^20.0.0", From e3cff3c707cae20f52811ef73bfb372e87a7c227 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:28:45 +0800 Subject: [PATCH 5/5] update lock file --- pnpm-lock.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c09235de9..f3e40be76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -237,9 +237,6 @@ importers: packages/plugins/tanstack-query: dependencies: - '@tanstack/angular-query-experimental': - specifier: ^5.0.0 - version: 5.84.1(@angular/common@20.1.4(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@20.1.4(rxjs@7.8.1)(zone.js@0.15.1)) '@zenstackhq/runtime': specifier: workspace:* version: link:../../runtime/dist