diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap index 3787efdea..b6a64b76e 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap @@ -697,7 +697,8 @@ import { CLIOptions, Inquirerer, extractFirst } from "inquirerer"; import { getClient } from "../executor"; import { coerceAnswers, parseFindFirstArgs, parseFindManyArgs, stripUndefined } from "../utils"; import type { FieldSchema } from "../utils"; -import type { CreateCarInput, CarPatch } from "../../orm/input-types"; +import type { CreateCarInput, CarPatch, CarSelect, CarFilter, CarsOrderBy } from "../../orm/input-types"; +import type { FindManyArgs, FindFirstArgs } from "../../orm/select-types"; const fieldSchema: FieldSchema = { id: "uuid", make: "string", @@ -756,7 +757,9 @@ async function handleList(argv: Partial>, _prompter: Inq isElectric: true, createdAt: true }; - const findManyArgs = parseFindManyArgs(argv, defaultSelect); + const findManyArgs = parseFindManyArgs & { + select: CarSelect; + }>(argv, defaultSelect); const client = getClient(); const result = await client.car.findMany(findManyArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -778,7 +781,9 @@ async function handleFindFirst(argv: Partial>, _prompter isElectric: true, createdAt: true }; - const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); + const findFirstArgs = parseFindFirstArgs & { + select: CarSelect; + }>(argv, defaultSelect); const client = getClient(); const result = await client.car.findFirst(findFirstArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -1160,7 +1165,8 @@ import { CLIOptions, Inquirerer, extractFirst } from "inquirerer"; import { getClient } from "../executor"; import { coerceAnswers, parseFindFirstArgs, parseFindManyArgs, stripUndefined } from "../utils"; import type { FieldSchema } from "../utils"; -import type { CreateDriverInput, DriverPatch } from "../../orm/input-types"; +import type { CreateDriverInput, DriverPatch, DriverSelect, DriverFilter, DriversOrderBy } from "../../orm/input-types"; +import type { FindManyArgs, FindFirstArgs } from "../../orm/select-types"; const fieldSchema: FieldSchema = { id: "uuid", name: "string", @@ -1213,7 +1219,9 @@ async function handleList(argv: Partial>, _prompter: Inq name: true, licenseNumber: true }; - const findManyArgs = parseFindManyArgs(argv, defaultSelect); + const findManyArgs = parseFindManyArgs & { + select: DriverSelect; + }>(argv, defaultSelect); const client = getClient(); const result = await client.driver.findMany(findManyArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -1232,7 +1240,9 @@ async function handleFindFirst(argv: Partial>, _prompter name: true, licenseNumber: true }; - const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); + const findFirstArgs = parseFindFirstArgs & { + select: DriverSelect; + }>(argv, defaultSelect); const client = getClient(); const result = await client.driver.findFirst(findFirstArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3191,7 +3201,8 @@ import { CLIOptions, Inquirerer, extractFirst } from "inquirerer"; import { getClient } from "../../executor"; import { coerceAnswers, parseFindFirstArgs, parseFindManyArgs, stripUndefined } from "../../utils"; import type { FieldSchema } from "../../utils"; -import type { CreateUserInput, UserPatch } from "../../../orm/input-types"; +import type { CreateUserInput, UserPatch, UserSelect, UserFilter, UsersOrderBy } from "../../../orm/input-types"; +import type { FindManyArgs, FindFirstArgs } from "../../../orm/select-types"; const fieldSchema: FieldSchema = { id: "uuid", email: "string", @@ -3244,7 +3255,9 @@ async function handleList(argv: Partial>, _prompter: Inq email: true, name: true }; - const findManyArgs = parseFindManyArgs(argv, defaultSelect); + const findManyArgs = parseFindManyArgs & { + select: UserSelect; + }>(argv, defaultSelect); const client = getClient("auth"); const result = await client.user.findMany(findManyArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3263,7 +3276,9 @@ async function handleFindFirst(argv: Partial>, _prompter email: true, name: true }; - const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); + const findFirstArgs = parseFindFirstArgs & { + select: UserSelect; + }>(argv, defaultSelect); const client = getClient("auth"); const result = await client.user.findFirst(findFirstArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3420,7 +3435,8 @@ import { CLIOptions, Inquirerer, extractFirst } from "inquirerer"; import { getClient } from "../../executor"; import { coerceAnswers, parseFindFirstArgs, parseFindManyArgs, stripUndefined } from "../../utils"; import type { FieldSchema } from "../../utils"; -import type { CreateMemberInput, MemberPatch } from "../../../orm/input-types"; +import type { CreateMemberInput, MemberPatch, MemberSelect, MemberFilter, MembersOrderBy } from "../../../orm/input-types"; +import type { FindManyArgs, FindFirstArgs } from "../../../orm/select-types"; const fieldSchema: FieldSchema = { id: "uuid", role: "string" @@ -3471,7 +3487,9 @@ async function handleList(argv: Partial>, _prompter: Inq id: true, role: true }; - const findManyArgs = parseFindManyArgs(argv, defaultSelect); + const findManyArgs = parseFindManyArgs & { + select: MemberSelect; + }>(argv, defaultSelect); const client = getClient("members"); const result = await client.member.findMany(findManyArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3489,7 +3507,9 @@ async function handleFindFirst(argv: Partial>, _prompter id: true, role: true }; - const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); + const findFirstArgs = parseFindFirstArgs & { + select: MemberSelect; + }>(argv, defaultSelect); const client = getClient("members"); const result = await client.member.findFirst(findFirstArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3631,7 +3651,8 @@ import { CLIOptions, Inquirerer, extractFirst } from "inquirerer"; import { getClient } from "../../executor"; import { coerceAnswers, parseFindFirstArgs, parseFindManyArgs, stripUndefined } from "../../utils"; import type { FieldSchema } from "../../utils"; -import type { CreateCarInput, CarPatch } from "../../../orm/input-types"; +import type { CreateCarInput, CarPatch, CarSelect, CarFilter, CarsOrderBy } from "../../../orm/input-types"; +import type { FindManyArgs, FindFirstArgs } from "../../../orm/select-types"; const fieldSchema: FieldSchema = { id: "uuid", make: "string", @@ -3690,7 +3711,9 @@ async function handleList(argv: Partial>, _prompter: Inq isElectric: true, createdAt: true }; - const findManyArgs = parseFindManyArgs(argv, defaultSelect); + const findManyArgs = parseFindManyArgs & { + select: CarSelect; + }>(argv, defaultSelect); const client = getClient("app"); const result = await client.car.findMany(findManyArgs).execute(); console.log(JSON.stringify(result, null, 2)); @@ -3712,7 +3735,9 @@ async function handleFindFirst(argv: Partial>, _prompter isElectric: true, createdAt: true }; - const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); + const findFirstArgs = parseFindFirstArgs & { + select: CarSelect; + }>(argv, defaultSelect); const client = getClient("app"); const result = await client.car.findFirst(findFirstArgs).execute(); console.log(JSON.stringify(result, null, 2)); diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap index c90ff15df..dca52f449 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap @@ -373,31 +373,6 @@ export interface CommentFilter { or?: CommentFilter[]; not?: CommentFilter; } -// ============ Table Condition Types ============ -export interface UserCondition { - id?: string | null; - email?: string | null; - name?: string | null; - age?: number | null; - isActive?: boolean | null; - createdAt?: string | null; - metadata?: unknown | null; -} -export interface PostCondition { - id?: string | null; - title?: string | null; - content?: string | null; - authorId?: string | null; - publishedAt?: string | null; - tags?: string | null; -} -export interface CommentCondition { - id?: string | null; - body?: string | null; - postId?: string | null; - authorId?: string | null; - createdAt?: string | null; -} // ============ OrderBy Types ============ export type UsersOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "AGE_ASC" | "AGE_DESC" | "IS_ACTIVE_ASC" | "IS_ACTIVE_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "METADATA_ASC" | "METADATA_DESC"; export type PostsOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "TITLE_ASC" | "TITLE_DESC" | "CONTENT_ASC" | "CONTENT_DESC" | "AUTHOR_ID_ASC" | "AUTHOR_ID_DESC" | "PUBLISHED_AT_ASC" | "PUBLISHED_AT_DESC" | "TAGS_ASC" | "TAGS_DESC"; @@ -771,16 +746,6 @@ export interface UserFilter { or?: UserFilter[]; not?: UserFilter; } -// ============ Table Condition Types ============ -export interface UserCondition { - id?: string | null; - email?: string | null; - name?: string | null; - age?: number | null; - isActive?: boolean | null; - createdAt?: string | null; - metadata?: unknown | null; -} // ============ OrderBy Types ============ export type UsersOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "AGE_ASC" | "AGE_DESC" | "IS_ACTIVE_ASC" | "IS_ACTIVE_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "METADATA_ASC" | "METADATA_DESC"; // ============ CRUD Input Types ============ @@ -1096,16 +1061,6 @@ export interface UserFilter { or?: UserFilter[]; not?: UserFilter; } -// ============ Table Condition Types ============ -export interface UserCondition { - id?: string | null; - email?: string | null; - name?: string | null; - age?: number | null; - isActive?: boolean | null; - createdAt?: string | null; - metadata?: unknown | null; -} // ============ OrderBy Types ============ export type UsersOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "AGE_ASC" | "AGE_DESC" | "IS_ACTIVE_ASC" | "IS_ACTIVE_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "METADATA_ASC" | "METADATA_DESC"; // ============ CRUD Input Types ============ @@ -1433,16 +1388,6 @@ export interface UserFilter { or?: UserFilter[]; not?: UserFilter; } -// ============ Table Condition Types ============ -export interface UserCondition { - id?: string | null; - email?: string | null; - name?: string | null; - age?: number | null; - isActive?: boolean | null; - createdAt?: string | null; - metadata?: unknown | null; -} // ============ OrderBy Types ============ export type UsersOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "AGE_ASC" | "AGE_DESC" | "IS_ACTIVE_ASC" | "IS_ACTIVE_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "METADATA_ASC" | "METADATA_DESC"; // ============ CRUD Input Types ============ @@ -1815,22 +1760,6 @@ export interface ProfileFilter { or?: ProfileFilter[]; not?: ProfileFilter; } -// ============ Table Condition Types ============ -export interface UserCondition { - id?: string | null; - email?: string | null; - name?: string | null; - age?: number | null; - isActive?: boolean | null; - createdAt?: string | null; - metadata?: unknown | null; -} -export interface ProfileCondition { - id?: string | null; - bio?: string | null; - userId?: string | null; - avatarUrl?: string | null; -} // ============ OrderBy Types ============ export type UsersOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "AGE_ASC" | "AGE_DESC" | "IS_ACTIVE_ASC" | "IS_ACTIVE_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "METADATA_ASC" | "METADATA_DESC"; export type ProfilesOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "BIO_ASC" | "BIO_DESC" | "USER_ID_ASC" | "USER_ID_DESC" | "AVATAR_URL_ASC" | "AVATAR_URL_DESC"; @@ -2210,20 +2139,6 @@ export interface CategoryFilter { or?: CategoryFilter[]; not?: CategoryFilter; } -// ============ Table Condition Types ============ -export interface PostCondition { - id?: string | null; - title?: string | null; - content?: string | null; - authorId?: string | null; - publishedAt?: string | null; - tags?: string | null; -} -export interface CategoryCondition { - id?: string | null; - name?: string | null; - slug?: string | null; -} // ============ OrderBy Types ============ export type PostsOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "TITLE_ASC" | "TITLE_DESC" | "CONTENT_ASC" | "CONTENT_DESC" | "AUTHOR_ID_ASC" | "AUTHOR_ID_DESC" | "PUBLISHED_AT_ASC" | "PUBLISHED_AT_DESC" | "TAGS_ASC" | "TAGS_DESC"; export type CategoriesOrderBy = "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC" | "NATURAL" | "ID_ASC" | "ID_DESC" | "NAME_ASC" | "NAME_DESC" | "SLUG_ASC" | "SLUG_DESC"; diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap index 4153840aa..18824756c 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap @@ -9,11 +9,11 @@ exports[`model-generator generates model with all CRUD methods 1`] = ` import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; -import type { User, UserWithRelations, UserSelect, UserFilter, UserCondition, UsersOrderBy, CreateUserInput, UpdateUserInput, UserPatch } from "../input-types"; +import type { User, UserWithRelations, UserSelect, UserFilter, UsersOrderBy, CreateUserInput, UpdateUserInput, UserPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; export class UserModel { constructor(private client: OrmClient) {} - findMany(args: FindManyArgs & { + findMany(args: FindManyArgs & { select: S; } & StrictSelect): QueryBuilder<{ users: ConnectionResult>; @@ -23,14 +23,13 @@ export class UserModel { variables } = buildFindManyDocument("User", "users", args.select, { where: args?.where, - condition: args?.condition, orderBy: args?.orderBy as string[] | undefined, first: args?.first, last: args?.last, after: args?.after, before: args?.before, offset: args?.offset - }, "UserFilter", "UsersOrderBy", connectionFieldsMap, "UserCondition"); + }, "UserFilter", "UsersOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -40,7 +39,7 @@ export class UserModel { variables }); } - findFirst(args: FindFirstArgs & { + findFirst(args: FindFirstArgs & { select: S; } & StrictSelect): QueryBuilder<{ users: { @@ -51,9 +50,8 @@ export class UserModel { document, variables } = buildFindFirstDocument("User", "users", args.select, { - where: args?.where, - condition: args?.condition - }, "UserFilter", connectionFieldsMap, "UserCondition"); + where: args?.where + }, "UserFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -160,11 +158,11 @@ exports[`model-generator generates model without update/delete when not availabl import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; -import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, AuditLogCondition, AuditLogsOrderBy, CreateAuditLogInput, UpdateAuditLogInput, AuditLogPatch } from "../input-types"; +import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, AuditLogsOrderBy, CreateAuditLogInput, UpdateAuditLogInput, AuditLogPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; export class AuditLogModel { constructor(private client: OrmClient) {} - findMany(args: FindManyArgs & { + findMany(args: FindManyArgs & { select: S; } & StrictSelect): QueryBuilder<{ auditLogs: ConnectionResult>; @@ -174,14 +172,13 @@ export class AuditLogModel { variables } = buildFindManyDocument("AuditLog", "auditLogs", args.select, { where: args?.where, - condition: args?.condition, orderBy: args?.orderBy as string[] | undefined, first: args?.first, last: args?.last, after: args?.after, before: args?.before, offset: args?.offset - }, "AuditLogFilter", "AuditLogsOrderBy", connectionFieldsMap, "AuditLogCondition"); + }, "AuditLogFilter", "AuditLogsOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -191,7 +188,7 @@ export class AuditLogModel { variables }); } - findFirst(args: FindFirstArgs & { + findFirst(args: FindFirstArgs & { select: S; } & StrictSelect): QueryBuilder<{ auditLogs: { @@ -202,9 +199,8 @@ export class AuditLogModel { document, variables } = buildFindFirstDocument("AuditLog", "auditLogs", args.select, { - where: args?.where, - condition: args?.condition - }, "AuditLogFilter", connectionFieldsMap, "AuditLogCondition"); + where: args?.where + }, "AuditLogFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -265,11 +261,11 @@ exports[`model-generator handles custom query/mutation names 1`] = ` import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; -import type { Organization, OrganizationWithRelations, OrganizationSelect, OrganizationFilter, OrganizationCondition, OrganizationsOrderBy, CreateOrganizationInput, UpdateOrganizationInput, OrganizationPatch } from "../input-types"; +import type { Organization, OrganizationWithRelations, OrganizationSelect, OrganizationFilter, OrganizationsOrderBy, CreateOrganizationInput, UpdateOrganizationInput, OrganizationPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; export class OrganizationModel { constructor(private client: OrmClient) {} - findMany(args: FindManyArgs & { + findMany(args: FindManyArgs & { select: S; } & StrictSelect): QueryBuilder<{ allOrganizations: ConnectionResult>; @@ -279,14 +275,13 @@ export class OrganizationModel { variables } = buildFindManyDocument("Organization", "allOrganizations", args.select, { where: args?.where, - condition: args?.condition, orderBy: args?.orderBy as string[] | undefined, first: args?.first, last: args?.last, after: args?.after, before: args?.before, offset: args?.offset - }, "OrganizationFilter", "OrganizationsOrderBy", connectionFieldsMap, "OrganizationCondition"); + }, "OrganizationFilter", "OrganizationsOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -296,7 +291,7 @@ export class OrganizationModel { variables }); } - findFirst(args: FindFirstArgs & { + findFirst(args: FindFirstArgs & { select: S; } & StrictSelect): QueryBuilder<{ allOrganizations: { @@ -307,9 +302,8 @@ export class OrganizationModel { document, variables } = buildFindFirstDocument("Organization", "allOrganizations", args.select, { - where: args?.where, - condition: args?.condition - }, "OrganizationFilter", connectionFieldsMap, "OrganizationCondition"); + where: args?.where + }, "OrganizationFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index 1ed497d76..16ed2c736 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -1436,9 +1436,9 @@ import { getClient } from "../client"; import { buildListSelectionArgs } from "../selection"; import type { ListSelectionConfig } from "../selection"; import { userKeys } from "../query-keys"; -import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy, UserCondition } from "../../orm/input-types"; +import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, HookStrictSelect } from "../../orm/select-types"; -export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy, UserCondition } from "../../orm/input-types"; +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; /** Query key factory - re-exported from query-keys.ts */ export const usersQueryKey = userKeys.list; /** @@ -1545,9 +1545,9 @@ import { buildListSelectionArgs } from "../selection"; import type { ListSelectionConfig } from "../selection"; import { postKeys } from "../query-keys"; import type { PostScope } from "../query-keys"; -import type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy, PostCondition } from "../../orm/input-types"; +import type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, HookStrictSelect } from "../../orm/select-types"; -export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy, PostCondition } from "../../orm/input-types"; +export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; /** Query key factory - re-exported from query-keys.ts */ export const postsQueryKey = postKeys.list; /** @@ -1669,10 +1669,10 @@ import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/rea import { getClient } from "../client"; import { buildListSelectionArgs } from "../selection"; import type { ListSelectionConfig } from "../selection"; -import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy, UserCondition } from "../../orm/input-types"; +import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, HookStrictSelect } from "../../orm/select-types"; -export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy, UserCondition } from "../../orm/input-types"; -export const usersQueryKey = (variables?: FindManyArgs) => ["user", "list", variables] as const; +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; +export const usersQueryKey = (variables?: FindManyArgs) => ["user", "list", variables] as const; /** * Query hook for fetching User list * diff --git a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts index 89cc8fed4..995b80767 100644 --- a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts @@ -965,7 +965,7 @@ describe('plugin-injected condition fields', () => { }, }); - const result = generateInputTypesFile(registry, new Set(), [contactTable]); + const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true }); // Regular table column fields should still be present expect(result.content).toContain('export interface ContactCondition {'); @@ -1025,7 +1025,7 @@ describe('plugin-injected condition fields', () => { }, }); - const result = generateInputTypesFile(registry, new Set(), [contactTable]); + const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true }); // VectorNearbyInput should be generated (follows *Input pattern) expect(result.content).toContain('export interface VectorNearbyInput {'); @@ -1051,7 +1051,7 @@ describe('plugin-injected condition fields', () => { }, }); - const result = generateInputTypesFile(registry, new Set(), [contactTable]); + const result = generateInputTypesFile(registry, new Set(), [contactTable], undefined, true, { condition: true }); // Count occurrences of 'id?' in the ContactCondition interface const conditionMatch = result.content.match( @@ -1102,7 +1102,7 @@ describe('plugin-injected condition fields', () => { it('works without typeRegistry (backwards compatible)', () => { // When no typeRegistry has the condition type, only table columns are used - const result = generateInputTypesFile(new Map(), new Set(), [contactTable]); + const result = generateInputTypesFile(new Map(), new Set(), [contactTable], undefined, true, { condition: true }); expect(result.content).toContain('export interface ContactCondition {'); expect(result.content).toContain('id?: string | null;'); diff --git a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts index b2efd99c8..8b111ebad 100644 --- a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts @@ -249,7 +249,7 @@ describe('model-generator', () => { }, }); - const result = generateModelFile(table, false); + const result = generateModelFile(table, false, { condition: true }); // Condition type should be imported expect(result.content).toContain('ContactCondition'); diff --git a/graphql/codegen/src/core/codegen/cli/index.ts b/graphql/codegen/src/core/codegen/cli/index.ts index 9e107db1a..fb47040a4 100644 --- a/graphql/codegen/src/core/codegen/cli/index.ts +++ b/graphql/codegen/src/core/codegen/cli/index.ts @@ -78,9 +78,12 @@ export function generateCli(options: GenerateCliOptions): GenerateCliResult { const authFile = generateAuthCommand(toolName); files.push(authFile); + const conditionEnabled = config.codegen?.condition === true; + for (const table of tables) { const tableFile = generateTableCommand(table, { typeRegistry: options.typeRegistry, + condition: conditionEnabled, }); files.push(tableFile); } @@ -143,6 +146,8 @@ export interface GenerateMultiTargetCliOptions { nodeHttpAdapter?: boolean; /** Generate a runnable index.ts entry point */ entryPoint?: boolean; + /** Whether PostGraphile condition types are enabled (default: true) */ + condition?: boolean; } export function resolveBuiltinNames( @@ -170,6 +175,7 @@ export function generateMultiTargetCli( options: GenerateMultiTargetCliOptions, ): GenerateCliResult { const { toolName, targets } = options; + const conditionEnabled = options.condition === true; const files: GeneratedFile[] = []; const targetNames = targets.map((t) => t.name); @@ -244,6 +250,7 @@ export function generateMultiTargetCli( targetName: target.name, executorImportPath: '../../executor', typeRegistry: target.typeRegistry, + condition: conditionEnabled, }); files.push(tableFile); } diff --git a/graphql/codegen/src/core/codegen/cli/table-command-generator.ts b/graphql/codegen/src/core/codegen/cli/table-command-generator.ts index 9bb158724..fa159a11a 100644 --- a/graphql/codegen/src/core/codegen/cli/table-command-generator.ts +++ b/graphql/codegen/src/core/codegen/cli/table-command-generator.ts @@ -15,6 +15,9 @@ import { toPascalCase, getCreateInputTypeName, getPatchTypeName, + getFilterTypeName, + getOrderByTypeName, + getConditionTypeName, } from '../utils'; import type { Table, TypeRegistry } from '../../../types/schema'; import type { GeneratedFile } from './executor-generator'; @@ -480,7 +483,84 @@ function buildAutoEmbedInputBlock( ); } -function buildListHandler(table: Table, vectorFieldNames: string[], targetName?: string, typeRegistry?: TypeRegistry): t.FunctionDeclaration { +/** + * Build the FindManyArgs type instantiation for a table: + * FindManyArgs & { select: SelectType } + * + * The intersection with { select: SelectType } makes select required, + * matching what the ORM's findMany method expects. parseFindManyArgs + * always sets select at runtime (from defaultSelect or --select flag). + */ +function buildFindManyArgsType(table: Table, conditionEnabled: boolean): t.TSType { + const { typeName } = getTableNames(table); + const selectTypeName = `${typeName}Select`; + const whereTypeName = getFilterTypeName(table); + const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined; + const orderByTypeName = getOrderByTypeName(table); + const findManyType = t.tsTypeReference( + t.identifier('FindManyArgs'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(whereTypeName)), + conditionTypeName + ? t.tsTypeReference(t.identifier(conditionTypeName)) + : t.tsNeverKeyword(), + t.tsTypeReference(t.identifier(orderByTypeName)), + ]), + ); + // Intersect with { select: SelectType } to make select required + return t.tsIntersectionType([ + findManyType, + t.tsTypeLiteral([ + Object.assign( + t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))), + ), + { optional: false }, + ), + ]), + ]); +} + +/** + * Build the FindFirstArgs type instantiation for a table: + * FindFirstArgs & { select: SelectType } + * + * The intersection with { select: SelectType } makes select required, + * matching what the ORM's findFirst method expects. + */ +function buildFindFirstArgsType(table: Table, conditionEnabled: boolean): t.TSType { + const { typeName } = getTableNames(table); + const selectTypeName = `${typeName}Select`; + const whereTypeName = getFilterTypeName(table); + const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined; + const findFirstType = t.tsTypeReference( + t.identifier('FindFirstArgs'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(whereTypeName)), + conditionTypeName + ? t.tsTypeReference(t.identifier(conditionTypeName)) + : t.tsNeverKeyword(), + ]), + ); + // Intersect with { select: SelectType } to make select required + return t.tsIntersectionType([ + findFirstType, + t.tsTypeLiteral([ + Object.assign( + t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))), + ), + { optional: false }, + ), + ]), + ]); +} + +function buildListHandler(table: Table, vectorFieldNames: string[], targetName?: string, typeRegistry?: TypeRegistry, conditionEnabled = false): t.FunctionDeclaration { const { singularName } = getTableNames(table); const defaultSelectObj = buildSelectObject(table, typeRegistry); @@ -494,18 +574,21 @@ function buildListHandler(table: Table, vectorFieldNames: string[], targetName?: ]), ); - // const findManyArgs = parseFindManyArgs(argv, defaultSelect); - tryBody.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('findManyArgs'), - t.callExpression(t.identifier('parseFindManyArgs'), [ - t.identifier('argv'), - t.identifier('defaultSelect'), - ]), - ), - ]), - ); + // const findManyArgs = parseFindManyArgs>(argv, defaultSelect); + { + const callExpr = t.callExpression(t.identifier('parseFindManyArgs'), [ + t.identifier('argv'), + t.identifier('defaultSelect'), + ]); + callExpr.typeParameters = t.tsTypeParameterInstantiation([ + buildFindManyArgsType(table, conditionEnabled), + ]); + tryBody.push( + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier('findManyArgs'), callExpr), + ]), + ); + } // Auto-embed vector fields in the where clause when --auto-embed is passed if (vectorFieldNames.length > 0) { @@ -575,7 +658,7 @@ function buildListHandler(table: Table, vectorFieldNames: string[], targetName?: * Accepts --select, --where.., --condition.. flags. * Internally calls findMany with first:1 and returns a single record (or null). */ -function buildFindFirstHandler(table: Table, targetName?: string, typeRegistry?: TypeRegistry): t.FunctionDeclaration { +function buildFindFirstHandler(table: Table, targetName?: string, typeRegistry?: TypeRegistry, conditionEnabled = false): t.FunctionDeclaration { const { singularName } = getTableNames(table); const defaultSelectObj = buildSelectObject(table, typeRegistry); @@ -588,18 +671,21 @@ function buildFindFirstHandler(table: Table, targetName?: string, typeRegistry?: ]), ); - // const findFirstArgs = parseFindFirstArgs(argv, defaultSelect); - tryBody.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('findFirstArgs'), - t.callExpression(t.identifier('parseFindFirstArgs'), [ - t.identifier('argv'), - t.identifier('defaultSelect'), - ]), - ), - ]), - ); + // const findFirstArgs = parseFindFirstArgs>(argv, defaultSelect); + { + const callExpr = t.callExpression(t.identifier('parseFindFirstArgs'), [ + t.identifier('argv'), + t.identifier('defaultSelect'), + ]); + callExpr.typeParameters = t.tsTypeParameterInstantiation([ + buildFindFirstArgsType(table, conditionEnabled), + ]); + tryBody.push( + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier('findFirstArgs'), callExpr), + ]), + ); + } tryBody.push(buildGetClientStatement(targetName)); @@ -662,6 +748,7 @@ function buildSearchHandler( vectorFieldNames: string[], targetName?: string, typeRegistry?: TypeRegistry, + conditionEnabled = false, ): t.FunctionDeclaration { const { singularName } = getTableNames(table); const defaultSelectObj = buildSelectObject(table, typeRegistry); @@ -808,19 +895,22 @@ function buildSearchHandler( ]), ); - // const findManyArgs = parseFindManyArgs(argv, defaultSelect, searchWhere); - tryBody.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('findManyArgs'), - t.callExpression(t.identifier('parseFindManyArgs'), [ - t.identifier('argv'), - t.identifier('defaultSelect'), - t.identifier('searchWhere'), - ]), - ), - ]), - ); + // const findManyArgs = parseFindManyArgs>(argv, defaultSelect, searchWhere); + { + const callExpr = t.callExpression(t.identifier('parseFindManyArgs'), [ + t.identifier('argv'), + t.identifier('defaultSelect'), + t.identifier('searchWhere'), + ]); + callExpr.typeParameters = t.tsTypeParameterInstantiation([ + buildFindManyArgsType(table, conditionEnabled), + ]); + tryBody.push( + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier('findManyArgs'), callExpr), + ]), + ); + } tryBody.push(buildGetClientStatement(targetName)); @@ -1247,10 +1337,12 @@ export interface TableCommandOptions { executorImportPath?: string; /** TypeRegistry from introspection, used to check field defaults */ typeRegistry?: TypeRegistry; + /** Whether PostGraphile condition types are enabled (default: true) */ + condition?: boolean; } export function generateTableCommand(table: Table, options?: TableCommandOptions): GeneratedFile { - const { singularName } = getTableNames(table); + const { singularName, typeName } = getTableNames(table); const commandName = toKebabCase(singularName); const statements: t.Statement[] = []; const executorPath = options?.executorImportPath ?? '../executor'; @@ -1286,8 +1378,29 @@ export function generateTableCommand(table: Table, options?: TableCommandOptions const inputTypesPath = options?.targetName ? `../../../orm/input-types` : `../../orm/input-types`; + // Import table-specific ORM types for generic type parameters on parseFindManyArgs/parseFindFirstArgs + const selectTypeName = `${typeName}Select`; + const whereTypeName = getFilterTypeName(table); + const orderByTypeName = getOrderByTypeName(table); + const conditionEnabled = options?.condition === true; + const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined; + statements.push( + createImportDeclaration(inputTypesPath, [ + createInputTypeName, + patchTypeName, + selectTypeName, + whereTypeName, + ...(conditionTypeName ? [conditionTypeName] : []), + orderByTypeName, + ], true), + ); + + // Import FindManyArgs/FindFirstArgs from select-types for proper generic typing + const selectTypesPath = options?.targetName + ? `../../../orm/select-types` + : `../../orm/select-types`; statements.push( - createImportDeclaration(inputTypesPath, [createInputTypeName, patchTypeName], true), + createImportDeclaration(selectTypesPath, ['FindManyArgs', 'FindFirstArgs'], true), ); // Generate field schema for type coercion @@ -1579,9 +1692,9 @@ export function generateTableCommand(table: Table, options?: TableCommandOptions const tn = options?.targetName; const ormTypes = { createInputTypeName, patchTypeName, innerFieldName }; - statements.push(buildListHandler(table, vectorFieldNames, tn, options?.typeRegistry)); - statements.push(buildFindFirstHandler(table, tn, options?.typeRegistry)); - if (hasSearchFields) statements.push(buildSearchHandler(table, specialGroups, vectorFieldNames, tn, options?.typeRegistry)); + statements.push(buildListHandler(table, vectorFieldNames, tn, options?.typeRegistry, conditionEnabled)); + statements.push(buildFindFirstHandler(table, tn, options?.typeRegistry, conditionEnabled)); + if (hasSearchFields) statements.push(buildSearchHandler(table, specialGroups, vectorFieldNames, tn, options?.typeRegistry, conditionEnabled)); if (hasGet) statements.push(buildGetHandler(table, tn, options?.typeRegistry)); statements.push(buildMutationHandler(table, 'create', vectorFieldNames, tn, options?.typeRegistry, ormTypes)); if (hasUpdate) statements.push(buildMutationHandler(table, 'update', vectorFieldNames, tn, options?.typeRegistry, ormTypes)); diff --git a/graphql/codegen/src/core/codegen/index.ts b/graphql/codegen/src/core/codegen/index.ts index d8960c2b5..d51a0260d 100644 --- a/graphql/codegen/src/core/codegen/index.ts +++ b/graphql/codegen/src/core/codegen/index.ts @@ -191,7 +191,7 @@ export function generate(options: GenerateOptions): GenerateResult { } // Condition types (PostGraphile simple equality filters) - const conditionEnabled = config.codegen?.condition !== false; + const conditionEnabled = config.codegen?.condition === true; // 4. Generate table-based query hooks (queries/*.ts) const queryHooks = generateAllQueryHooks(tables, { diff --git a/graphql/codegen/src/core/codegen/orm/index.ts b/graphql/codegen/src/core/codegen/orm/index.ts index aa0d1b87e..b46f28f60 100644 --- a/graphql/codegen/src/core/codegen/orm/index.ts +++ b/graphql/codegen/src/core/codegen/orm/index.ts @@ -66,7 +66,7 @@ export interface GenerateOrmResult { export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { const { tables, customOperations, sharedTypesPath } = options; const commentsEnabled = options.config.codegen?.comments !== false; - const conditionEnabled = options.config.codegen?.condition !== false; + const conditionEnabled = options.config.codegen?.condition === true; const files: GeneratedFile[] = []; // Use shared types when a sharedTypesPath is provided (unified output mode) diff --git a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts index 071a8ad15..84d6c2c3e 100644 --- a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts @@ -2093,7 +2093,7 @@ export function generateInputTypesFile( comments: boolean = true, options?: { condition?: boolean }, ): GeneratedInputTypesFile { - const conditionEnabled = options?.condition !== false; + const conditionEnabled = options?.condition === true; const statements: t.Statement[] = []; const tablesList = tables ?? []; const hasTables = tablesList.length > 0; diff --git a/graphql/codegen/src/core/codegen/orm/model-generator.ts b/graphql/codegen/src/core/codegen/orm/model-generator.ts index d50fdcd7c..8d971b58d 100644 --- a/graphql/codegen/src/core/codegen/orm/model-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/model-generator.ts @@ -174,7 +174,7 @@ export function generateModelFile( options?: { condition?: boolean }, allTables?: Table[], ): GeneratedModelFile { - const conditionEnabled = options?.condition !== false; + const conditionEnabled = options?.condition === true; const { typeName, singularName, pluralName } = getTableNames(table); const modelName = `${typeName}Model`; const baseFileName = lcFirst(typeName); diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index 1b43251c2..326ba4519 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -87,7 +87,7 @@ export function generateListQueryHook( reactQueryEnabled = true, useCentralizedKeys = true, hasRelationships = false, - condition: conditionEnabled = true, + condition: conditionEnabled = false, } = options; const { typeName, pluralName, singularName } = getTableNames(table); const hookName = getListQueryHookName(table); diff --git a/graphql/codegen/src/core/codegen/templates/cli-utils.ts b/graphql/codegen/src/core/codegen/templates/cli-utils.ts index 3a8cac99f..fd0170d43 100644 --- a/graphql/codegen/src/core/codegen/templates/cli-utils.ts +++ b/graphql/codegen/src/core/codegen/templates/cli-utils.ts @@ -252,11 +252,11 @@ export function parseSelectFlag( * const findManyArgs = parseFindManyArgs(argv, { id: true, name: true }); * const result = await client.user.findMany(findManyArgs).execute(); */ -export function parseFindManyArgs( +export function parseFindManyArgs>( argv: Record, defaultSelect: Record, extraWhere?: Record, -): Record { +): T { const limit = parseIntFlag(argv, 'limit'); const last = parseIntFlag(argv, 'last'); const offset = parseIntFlag(argv, 'offset'); @@ -280,7 +280,7 @@ export function parseFindManyArgs( ...(where !== undefined ? { where } : {}), ...(condition !== undefined ? { condition } : {}), ...(orderBy !== undefined ? { orderBy } : {}), - }; + } as unknown as T; } /** @@ -288,10 +288,10 @@ export function parseFindManyArgs( * Like parseFindManyArgs but only includes select, where, and condition * (no pagination flags — findFirst returns the first matching record). */ -export function parseFindFirstArgs( +export function parseFindFirstArgs>( argv: Record, defaultSelect: Record, -): Record { +): T { const select = parseSelectFlag(argv, defaultSelect); const parsed = unflattenDotNotation(argv); const where = parsed.where; @@ -301,7 +301,7 @@ export function parseFindFirstArgs( select, ...(where !== undefined ? { where } : {}), ...(condition !== undefined ? { condition } : {}), - }; + } as unknown as T; } export function buildSelectFromPaths( diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index f2f760991..cf4e4be5e 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -714,12 +714,14 @@ export async function generateMulti( firstTargetConfig?.nodeHttpAdapter === true || (firstTargetConfig?.nodeHttpAdapter !== false); + const multiConditionEnabled = firstTargetConfig?.codegen?.condition === true; const { files } = generateMultiTargetCli({ toolName, builtinNames: cliConfig.builtinNames, targets: cliTargets, nodeHttpAdapter: multiNodeHttpAdapter, entryPoint: cliConfig.entryPoint, + condition: multiConditionEnabled, }); const cliFilesToWrite = files.map((file) => ({