UNPKG

drizzle-graphql

Version:

Automatically generate GraphQL schema or customizable schema config fields from Drizzle ORM schema

1 lines 123 kB
{"version":3,"sources":["../src/index.ts","../src/util/builders/mysql.ts","../src/util/builders/common.ts","../src/util/case-ops/index.ts","../src/util/data-mappers/index.ts","../src/util/type-converter/index.ts","../src/util/builders/pg.ts","../src/util/builders/sqlite.ts"],"sourcesContent":["import { is } from 'drizzle-orm';\nimport { MySqlDatabase } from 'drizzle-orm/mysql-core';\nimport { PgDatabase } from 'drizzle-orm/pg-core';\nimport { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';\nimport {\n\tGraphQLFieldConfig,\n\tGraphQLInputObjectType,\n\tGraphQLObjectType,\n\tGraphQLSchema,\n\tGraphQLSchemaConfig,\n} from 'graphql';\n\nimport { generateMySQL, generatePG, generateSQLite } from '@/util/builders';\nimport { ObjMap } from 'graphql/jsutils/ObjMap';\nimport type { AnyDrizzleDB, BuildSchemaConfig, GeneratedData } from './types';\n\nexport const buildSchema = <TDbClient extends AnyDrizzleDB<any>>(\n\tdb: TDbClient,\n\tconfig?: BuildSchemaConfig,\n): GeneratedData<TDbClient> => {\n\tconst schema = db._.fullSchema;\n\tif (!schema) {\n\t\tthrow new Error(\n\t\t\t\"Drizzle-GraphQL Error: Schema not found in drizzle instance. Make sure you're using drizzle-orm v0.30.9 or above and schema is passed to drizzle constructor!\",\n\t\t);\n\t}\n\n\tif (typeof config?.relationsDepthLimit === 'number') {\n\t\tif (config.relationsDepthLimit < 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Drizzle-GraphQL Error: config.relationsDepthLimit is supposed to be nonnegative integer or undefined!',\n\t\t\t);\n\t\t}\n\t\tif (config.relationsDepthLimit !== ~~config.relationsDepthLimit) {\n\t\t\tthrow new Error(\n\t\t\t\t'Drizzle-GraphQL Error: config.relationsDepthLimit is supposed to be nonnegative integer or undefined!',\n\t\t\t);\n\t\t}\n\t}\n\n\tlet generatorOutput;\n\tif (is(db, MySqlDatabase)) {\n\t\tgeneratorOutput = generateMySQL(db, schema, config?.relationsDepthLimit);\n\t} else if (is(db, PgDatabase)) {\n\t\tgeneratorOutput = generatePG(db, schema, config?.relationsDepthLimit);\n\t} else if (is(db, BaseSQLiteDatabase)) {\n\t\tgeneratorOutput = generateSQLite(db, schema, config?.relationsDepthLimit);\n\t} else throw new Error('Drizzle-GraphQL Error: Unknown database instance type');\n\n\tconst { queries, mutations, inputs, types } = generatorOutput;\n\n\tconst graphQLSchemaConfig: GraphQLSchemaConfig = {\n\t\ttypes: [...Object.values(inputs), ...Object.values(types)] as (GraphQLInputObjectType | GraphQLObjectType)[],\n\t\tquery: new GraphQLObjectType({\n\t\t\tname: 'Query',\n\t\t\tfields: queries as ObjMap<GraphQLFieldConfig<any, any, any>>,\n\t\t}),\n\t};\n\n\tif (config?.mutations !== false) {\n\t\tconst mutation = new GraphQLObjectType({\n\t\t\tname: 'Mutation',\n\t\t\tfields: mutations as ObjMap<GraphQLFieldConfig<any, any, any>>,\n\t\t});\n\n\t\tgraphQLSchemaConfig.mutation = mutation;\n\t}\n\n\tconst outputSchema = new GraphQLSchema(graphQLSchemaConfig);\n\n\treturn { schema: outputSchema, entities: generatorOutput };\n};\n\nexport * from './types';\n","import { createTableRelationsHelpers, is, Relation, Relations, Table } from 'drizzle-orm';\nimport { MySqlDatabase, MySqlTable } from 'drizzle-orm/mysql-core';\nimport {\n\tGraphQLBoolean,\n\tGraphQLError,\n\tGraphQLInputObjectType,\n\tGraphQLInt,\n\tGraphQLList,\n\tGraphQLNonNull,\n\tGraphQLObjectType,\n} from 'graphql';\n\nimport {\n\textractFilters,\n\textractOrderBy,\n\textractRelationsParams,\n\textractSelectedColumnsFromTree,\n\tgenerateTableTypes,\n} from '@/util/builders/common';\nimport { capitalize, uncapitalize } from '@/util/case-ops';\nimport {\n\tremapFromGraphQLArrayInput,\n\tremapFromGraphQLSingleInput,\n\tremapToGraphQLArrayOutput,\n\tremapToGraphQLSingleOutput,\n} from '@/util/data-mappers';\nimport { parseResolveInfo } from 'graphql-parse-resolve-info';\n\nimport type { GeneratedEntities } from '@/types';\nimport type { RelationalQueryBuilder } from 'drizzle-orm/mysql-core/query-builders/query';\nimport type { GraphQLFieldConfig, GraphQLFieldConfigArgumentMap, ThunkObjMap } from 'graphql';\nimport type { ResolveTree } from 'graphql-parse-resolve-info';\nimport type { CreatedResolver, Filters, TableNamedRelations, TableSelectArgs } from './types';\n\nconst generateSelectArray = (\n\tdb: MySqlDatabase<any, any, any>,\n\ttableName: string,\n\ttables: Record<string, Table>,\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\torderArgs: GraphQLInputObjectType,\n\tfilterArgs: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `${uncapitalize(tableName)}`;\n\tconst queryBase = db.query[tableName as keyof typeof db.query] as unknown as\n\t\t| RelationalQueryBuilder<any, any, any>\n\t\t| undefined;\n\tif (!queryBase) {\n\t\tthrow new Error(\n\t\t\t`Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`,\n\t\t);\n\t}\n\n\tconst queryArgs = {\n\t\toffset: {\n\t\t\ttype: GraphQLInt,\n\t\t},\n\t\tlimit: {\n\t\t\ttype: GraphQLInt,\n\t\t},\n\t\torderBy: {\n\t\t\ttype: orderArgs,\n\t\t},\n\t\twhere: {\n\t\t\ttype: filterArgs,\n\t\t},\n\t} as GraphQLFieldConfigArgumentMap;\n\n\tconst typeName = `${capitalize(tableName)}SelectItem`;\n\tconst table = tables[tableName]!;\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: Partial<TableSelectArgs>, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst { offset, limit, orderBy, where } = args;\n\n\t\t\t\tconst parsedInfo = parseResolveInfo(info, {\n\t\t\t\t\tdeep: true,\n\t\t\t\t}) as ResolveTree;\n\n\t\t\t\tconst query = queryBase.findMany({\n\t\t\t\t\tcolumns: extractSelectedColumnsFromTree(\n\t\t\t\t\t\tparsedInfo.fieldsByTypeName[typeName]!,\n\t\t\t\t\t\ttable,\n\t\t\t\t\t),\n\t\t\t\t\toffset,\n\t\t\t\t\tlimit,\n\t\t\t\t\torderBy: orderBy ? extractOrderBy(table, orderBy) : undefined,\n\t\t\t\t\twhere: where ? extractFilters(table, tableName, where) : undefined,\n\t\t\t\t\twith: relationMap[tableName]\n\t\t\t\t\t\t? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\n\t\t\t\tconst result = await query;\n\n\t\t\t\treturn remapToGraphQLArrayOutput(result, tableName, table, relationMap);\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nconst generateSelectSingle = (\n\tdb: MySqlDatabase<any, any, any>,\n\ttableName: string,\n\ttables: Record<string, Table>,\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\torderArgs: GraphQLInputObjectType,\n\tfilterArgs: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `${uncapitalize(tableName)}Single`;\n\tconst queryBase = db.query[tableName as keyof typeof db.query] as unknown as\n\t\t| RelationalQueryBuilder<any, any, any>\n\t\t| undefined;\n\tif (!queryBase) {\n\t\tthrow new Error(\n\t\t\t`Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`,\n\t\t);\n\t}\n\n\tconst queryArgs = {\n\t\toffset: {\n\t\t\ttype: GraphQLInt,\n\t\t},\n\t\torderBy: {\n\t\t\ttype: orderArgs,\n\t\t},\n\t\twhere: {\n\t\t\ttype: filterArgs,\n\t\t},\n\t} as GraphQLFieldConfigArgumentMap;\n\n\tconst typeName = `${capitalize(tableName)}SelectItem`;\n\tconst table = tables[tableName]!;\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: Partial<TableSelectArgs>, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst { offset, orderBy, where } = args;\n\n\t\t\t\tconst parsedInfo = parseResolveInfo(info, {\n\t\t\t\t\tdeep: true,\n\t\t\t\t}) as ResolveTree;\n\n\t\t\t\tconst query = queryBase.findFirst({\n\t\t\t\t\tcolumns: extractSelectedColumnsFromTree(\n\t\t\t\t\t\tparsedInfo.fieldsByTypeName[typeName]!,\n\t\t\t\t\t\ttable,\n\t\t\t\t\t),\n\t\t\t\t\toffset,\n\t\t\t\t\torderBy: orderBy ? extractOrderBy(table, orderBy) : undefined,\n\t\t\t\t\twhere: where ? extractFilters(table, tableName, where) : undefined,\n\t\t\t\t\twith: relationMap[tableName]\n\t\t\t\t\t\t? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\n\t\t\t\tconst result = await query;\n\t\t\t\tif (!result) return undefined;\n\n\t\t\t\treturn remapToGraphQLSingleOutput(result, tableName, table, relationMap);\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nconst generateInsertArray = (\n\tdb: MySqlDatabase<any, any, any, any>,\n\ttableName: string,\n\ttable: MySqlTable,\n\tbaseType: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `insertInto${capitalize(tableName)}`;\n\n\tconst queryArgs: GraphQLFieldConfigArgumentMap = {\n\t\tvalues: {\n\t\t\ttype: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(baseType))),\n\t\t},\n\t};\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: { values: Record<string, any>[] }, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst input = remapFromGraphQLArrayInput(args.values, table);\n\t\t\t\tif (!input.length) throw new GraphQLError('No values were provided!');\n\n\t\t\t\tawait db.insert(table).values(input);\n\n\t\t\t\treturn { isSuccess: true };\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nconst generateInsertSingle = (\n\tdb: MySqlDatabase<any, any, any, any>,\n\ttableName: string,\n\ttable: MySqlTable,\n\tbaseType: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `insertInto${capitalize(tableName)}Single`;\n\n\tconst queryArgs: GraphQLFieldConfigArgumentMap = {\n\t\tvalues: {\n\t\t\ttype: new GraphQLNonNull(baseType),\n\t\t},\n\t};\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: { values: Record<string, any> }, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst input = remapFromGraphQLSingleInput(args.values, table);\n\n\t\t\t\tawait db.insert(table).values(input);\n\n\t\t\t\treturn { isSuccess: true };\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nconst generateUpdate = (\n\tdb: MySqlDatabase<any, any, any>,\n\ttableName: string,\n\ttable: MySqlTable,\n\tsetArgs: GraphQLInputObjectType,\n\tfilterArgs: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `update${capitalize(tableName)}`;\n\n\tconst queryArgs = {\n\t\tset: {\n\t\t\ttype: new GraphQLNonNull(setArgs),\n\t\t},\n\t\twhere: {\n\t\t\ttype: filterArgs,\n\t\t},\n\t} as const satisfies GraphQLFieldConfigArgumentMap;\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: { where?: Filters<Table>; set: Record<string, any> }, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst { where, set } = args;\n\n\t\t\t\tconst input = remapFromGraphQLSingleInput(set, table);\n\t\t\t\tif (!Object.keys(input).length) throw new GraphQLError('Unable to update with no values specified!');\n\n\t\t\t\tlet query = db.update(table).set(input);\n\t\t\t\tif (where) {\n\t\t\t\t\tconst filters = extractFilters(table, tableName, where);\n\t\t\t\t\tquery = query.where(filters) as any;\n\t\t\t\t}\n\n\t\t\t\tawait query;\n\n\t\t\t\treturn { isSuccess: true };\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nconst generateDelete = (\n\tdb: MySqlDatabase<any, any, any>,\n\ttableName: string,\n\ttable: MySqlTable,\n\tfilterArgs: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `deleteFrom${tableName}`;\n\n\tconst queryArgs = {\n\t\twhere: {\n\t\t\ttype: filterArgs,\n\t\t},\n\t} as const satisfies GraphQLFieldConfigArgumentMap;\n\n\treturn {\n\t\tname: queryName,\n\t\tresolver: async (source, args: { where?: Filters<Table> }, context, info) => {\n\t\t\ttry {\n\t\t\t\tconst { where } = args;\n\n\t\t\t\tlet query = db.delete(table);\n\t\t\t\tif (where) {\n\t\t\t\t\tconst filters = extractFilters(table, tableName, where);\n\t\t\t\t\tquery = query.where(filters) as any;\n\t\t\t\t}\n\n\t\t\t\tawait query;\n\n\t\t\t\treturn { isSuccess: true };\n\t\t\t} catch (e) {\n\t\t\t\tif (typeof e === 'object' && typeof (<any> e).message === 'string') {\n\t\t\t\t\tthrow new GraphQLError((<any> e).message);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\targs: queryArgs,\n\t};\n};\n\nexport const generateSchemaData = <\n\tTDrizzleInstance extends MySqlDatabase<any, any, any, any>,\n\tTSchema extends Record<string, Table | unknown>,\n>(\n\tdb: TDrizzleInstance,\n\tschema: TSchema,\n\trelationsDepthLimit: number | undefined,\n): GeneratedEntities<TDrizzleInstance, TSchema> => {\n\tconst rawSchema = schema;\n\tconst schemaEntries = Object.entries(rawSchema);\n\n\tconst tableEntries = schemaEntries.filter(([key, value]) => is(value, MySqlTable)) as [string, MySqlTable][];\n\tconst tables = Object.fromEntries(tableEntries);\n\n\tif (!tableEntries.length) {\n\t\tthrow new Error(\n\t\t\t\"Drizzle-GraphQL Error: No tables detected in Drizzle-ORM's database instance. Did you forget to pass schema to drizzle constructor?\",\n\t\t);\n\t}\n\n\tconst rawRelations = schemaEntries\n\t\t.filter(([key, value]) => is(value, Relations))\n\t\t.map<[string, Relations]>(([key, value]) => [\n\t\t\ttableEntries.find(\n\t\t\t\t([tableName, tableValue]) => tableValue === (value as Relations).table,\n\t\t\t)![0] as string,\n\t\t\tvalue as Relations,\n\t\t]).map<[string, Record<string, Relation>]>(([tableName, relValue]) => [\n\t\t\ttableName,\n\t\t\trelValue.config(createTableRelationsHelpers(tables[tableName]!)),\n\t\t]);\n\n\tconst namedRelations = Object.fromEntries(\n\t\trawRelations\n\t\t\t.map(([relName, config]) => {\n\t\t\t\tconst namedConfig: Record<string, TableNamedRelations> = Object.fromEntries(\n\t\t\t\t\tObject.entries(config).map(([innerRelName, innerRelValue]) => [innerRelName, {\n\t\t\t\t\t\trelation: innerRelValue,\n\t\t\t\t\t\ttargetTableName: tableEntries.find(([tableName, tableValue]) =>\n\t\t\t\t\t\t\ttableValue === innerRelValue.referencedTable\n\t\t\t\t\t\t)![0],\n\t\t\t\t\t}]),\n\t\t\t\t);\n\n\t\t\t\treturn [\n\t\t\t\t\trelName,\n\t\t\t\t\tnamedConfig,\n\t\t\t\t];\n\t\t\t}),\n\t);\n\n\tconst queries: ThunkObjMap<GraphQLFieldConfig<any, any>> = {};\n\tconst mutations: ThunkObjMap<GraphQLFieldConfig<any, any>> = {};\n\tconst gqlSchemaTypes = Object.fromEntries(\n\t\tObject.entries(tables).map(([tableName, table]) => [\n\t\t\ttableName,\n\t\t\tgenerateTableTypes(tableName, tables, namedRelations, false, relationsDepthLimit),\n\t\t]),\n\t);\n\n\tconst mutationReturnType = new GraphQLObjectType({\n\t\tname: `MutationReturn`,\n\t\tfields: {\n\t\t\tisSuccess: {\n\t\t\t\ttype: new GraphQLNonNull(GraphQLBoolean),\n\t\t\t},\n\t\t},\n\t});\n\n\tconst inputs: Record<string, GraphQLInputObjectType> = {};\n\tconst outputs: Record<string, GraphQLObjectType> = {\n\t\tMutationReturn: mutationReturnType,\n\t};\n\n\tfor (const [tableName, tableTypes] of Object.entries(gqlSchemaTypes)) {\n\t\tconst { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;\n\t\tconst { selectSingleOutput, selectArrOutput } = tableTypes.outputs;\n\n\t\tconst selectArrGenerated = generateSelectArray(\n\t\t\tdb,\n\t\t\ttableName,\n\t\t\ttables,\n\t\t\tnamedRelations,\n\t\t\ttableOrder,\n\t\t\ttableFilters,\n\t\t);\n\t\tconst selectSingleGenerated = generateSelectSingle(\n\t\t\tdb,\n\t\t\ttableName,\n\t\t\ttables,\n\t\t\tnamedRelations,\n\t\t\ttableOrder,\n\t\t\ttableFilters,\n\t\t);\n\t\tconst insertArrGenerated = generateInsertArray(db, tableName, schema[tableName] as MySqlTable, insertInput);\n\t\tconst insertSingleGenerated = generateInsertSingle(db, tableName, schema[tableName] as MySqlTable, insertInput);\n\t\tconst updateGenerated = generateUpdate(\n\t\t\tdb,\n\t\t\ttableName,\n\t\t\tschema[tableName] as MySqlTable,\n\t\t\tupdateInput,\n\t\t\ttableFilters,\n\t\t);\n\t\tconst deleteGenerated = generateDelete(db, tableName, schema[tableName] as MySqlTable, tableFilters);\n\n\t\tqueries[selectArrGenerated.name] = {\n\t\t\ttype: selectArrOutput,\n\t\t\targs: selectArrGenerated.args,\n\t\t\tresolve: selectArrGenerated.resolver,\n\t\t};\n\t\tqueries[selectSingleGenerated.name] = {\n\t\t\ttype: selectSingleOutput,\n\t\t\targs: selectSingleGenerated.args,\n\t\t\tresolve: selectSingleGenerated.resolver,\n\t\t};\n\t\tmutations[insertArrGenerated.name] = {\n\t\t\ttype: mutationReturnType,\n\t\t\targs: insertArrGenerated.args,\n\t\t\tresolve: insertArrGenerated.resolver,\n\t\t};\n\t\tmutations[insertSingleGenerated.name] = {\n\t\t\ttype: mutationReturnType,\n\t\t\targs: insertSingleGenerated.args,\n\t\t\tresolve: insertSingleGenerated.resolver,\n\t\t};\n\t\tmutations[updateGenerated.name] = {\n\t\t\ttype: mutationReturnType,\n\t\t\targs: updateGenerated.args,\n\t\t\tresolve: updateGenerated.resolver,\n\t\t};\n\t\tmutations[deleteGenerated.name] = {\n\t\t\ttype: mutationReturnType,\n\t\t\targs: deleteGenerated.args,\n\t\t\tresolve: deleteGenerated.resolver,\n\t\t};\n\t\t[insertInput, updateInput, tableFilters, tableOrder].forEach((e) => (inputs[e.name] = e));\n\t\toutputs[selectSingleOutput.name] = selectSingleOutput;\n\t}\n\n\treturn { queries, mutations, inputs, types: outputs } as any;\n};\n","import {\n\tand,\n\tasc,\n\tdesc,\n\teq,\n\tgetTableColumns,\n\tgt,\n\tgte,\n\tilike,\n\tinArray,\n\tis,\n\tisNotNull,\n\tisNull,\n\tlike,\n\tlt,\n\tlte,\n\tne,\n\tnotIlike,\n\tnotInArray,\n\tnotLike,\n\tOne,\n\tor,\n\tSQL,\n} from 'drizzle-orm';\nimport {\n\tGraphQLBoolean,\n\tGraphQLEnumType,\n\tGraphQLError,\n\tGraphQLInputObjectType,\n\tGraphQLInt,\n\tGraphQLList,\n\tGraphQLNonNull,\n\tGraphQLObjectType,\n\tGraphQLString,\n} from 'graphql';\n\nimport { capitalize } from '@/util/case-ops';\nimport { remapFromGraphQLCore } from '@/util/data-mappers';\nimport {\n\tConvertedColumn,\n\tConvertedInputColumn,\n\tConvertedRelationColumnWithArgs,\n\tdrizzleColumnToGraphQLType,\n} from '@/util/type-converter';\n\nimport type { Column, Table } from 'drizzle-orm';\nimport type { ResolveTree } from 'graphql-parse-resolve-info';\nimport type {\n\tFilterColumnOperators,\n\tFilterColumnOperatorsCore,\n\tFilters,\n\tFiltersCore,\n\tGeneratedTableTypes,\n\tGeneratedTableTypesOutputs,\n\tOrderByArgs,\n\tProcessedTableSelectArgs,\n\tSelectData,\n\tSelectedColumnsRaw,\n\tSelectedSQLColumns,\n\tTableNamedRelations,\n\tTableSelectArgs,\n} from './types';\n\nconst rqbCrashTypes = [\n\t'SQLiteBigInt',\n\t'SQLiteBlobJson',\n\t'SQLiteBlobBuffer',\n];\n\nexport const extractSelectedColumnsFromTree = (\n\ttree: Record<string, ResolveTree>,\n\ttable: Table,\n): Record<string, true> => {\n\tconst tableColumns = getTableColumns(table);\n\n\tconst treeEntries = Object.entries(tree);\n\tconst selectedColumns: SelectedColumnsRaw = [];\n\n\tfor (const [fieldName, fieldData] of treeEntries) {\n\t\tif (!tableColumns[fieldData.name]) continue;\n\n\t\tselectedColumns.push([fieldData.name, true]);\n\t}\n\n\tif (!selectedColumns.length) {\n\t\tconst columnKeys = Object.entries(tableColumns);\n\t\tconst columnName = columnKeys.find((e) => rqbCrashTypes.find((haram) => e[1].columnType !== haram))?.[0]\n\t\t\t?? columnKeys[0]![0];\n\n\t\tselectedColumns.push([columnName, true]);\n\t}\n\n\treturn Object.fromEntries(selectedColumns);\n};\n\n/**\n * Can't automatically determine column type on type level\n * Since drizzle table types extend eachother\n */\nexport const extractSelectedColumnsFromTreeSQLFormat = <TColType extends Column = Column>(\n\ttree: Record<string, ResolveTree>,\n\ttable: Table,\n): Record<string, TColType> => {\n\tconst tableColumns = getTableColumns(table);\n\n\tconst treeEntries = Object.entries(tree);\n\tconst selectedColumns: SelectedSQLColumns = [];\n\n\tfor (const [fieldName, fieldData] of treeEntries) {\n\t\tif (!tableColumns[fieldData.name]) continue;\n\n\t\tselectedColumns.push([fieldData.name, tableColumns[fieldData.name]!]);\n\t}\n\n\tif (!selectedColumns.length) {\n\t\tconst columnKeys = Object.entries(tableColumns);\n\t\tconst columnName = columnKeys.find((e) => rqbCrashTypes.find((haram) => e[1].columnType !== haram))?.[0]\n\t\t\t?? columnKeys[0]![0];\n\n\t\tselectedColumns.push([columnName, tableColumns[columnName]!]);\n\t}\n\n\treturn Object.fromEntries(selectedColumns) as Record<string, TColType>;\n};\n\nexport const innerOrder = new GraphQLInputObjectType({\n\tname: 'InnerOrder' as const,\n\tfields: {\n\t\tdirection: {\n\t\t\ttype: new GraphQLNonNull(\n\t\t\t\tnew GraphQLEnumType({\n\t\t\t\t\tname: 'OrderDirection',\n\t\t\t\t\tdescription: 'Order by direction',\n\t\t\t\t\tvalues: {\n\t\t\t\t\t\tasc: {\n\t\t\t\t\t\t\tvalue: 'asc',\n\t\t\t\t\t\t\tdescription: 'Ascending order',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdesc: {\n\t\t\t\t\t\t\tvalue: 'desc',\n\t\t\t\t\t\t\tdescription: 'Descending order',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\tpriority: { type: new GraphQLNonNull(GraphQLInt), description: 'Priority of current field' },\n\t} as const,\n});\n\nconst generateColumnFilterValues = (column: Column, tableName: string, columnName: string): GraphQLInputObjectType => {\n\tconst columnGraphQLType = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);\n\tconst columnArr = new GraphQLList(new GraphQLNonNull(columnGraphQLType.type));\n\n\tconst baseFields = {\n\t\teq: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tne: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tlt: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tlte: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tgt: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tgte: { type: columnGraphQLType.type, description: columnGraphQLType.description },\n\t\tlike: { type: GraphQLString },\n\t\tnotLike: { type: GraphQLString },\n\t\tilike: { type: GraphQLString },\n\t\tnotIlike: { type: GraphQLString },\n\t\tinArray: { type: columnArr, description: `Array<${columnGraphQLType.description}>` },\n\t\tnotInArray: { type: columnArr, description: `Array<${columnGraphQLType.description}>` },\n\t\tisNull: { type: GraphQLBoolean },\n\t\tisNotNull: { type: GraphQLBoolean },\n\t};\n\n\tconst type: GraphQLInputObjectType = new GraphQLInputObjectType({\n\t\tname: `${capitalize(tableName)}${capitalize(columnName)}Filters`,\n\t\tfields: {\n\t\t\t...baseFields,\n\t\t\tOR: {\n\t\t\t\ttype: new GraphQLList(\n\t\t\t\t\tnew GraphQLNonNull(\n\t\t\t\t\t\tnew GraphQLInputObjectType({\n\t\t\t\t\t\t\tname: `${capitalize(tableName)}${capitalize(columnName)}filtersOr`,\n\t\t\t\t\t\t\tfields: {\n\t\t\t\t\t\t\t\t...baseFields,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t});\n\n\treturn type;\n};\n\nconst orderMap = new WeakMap<Object, Record<string, ConvertedInputColumn>>();\nconst generateTableOrderCached = (table: Table) => {\n\tif (orderMap.has(table)) return orderMap.get(table)!;\n\n\tconst columns = getTableColumns(table);\n\tconst columnEntries = Object.entries(columns);\n\n\tconst remapped = Object.fromEntries(\n\t\tcolumnEntries.map(([columnName, columnDescription]) => [columnName, { type: innerOrder }]),\n\t);\n\n\torderMap.set(table, remapped);\n\n\treturn remapped;\n};\n\nconst filterMap = new WeakMap<Object, Record<string, ConvertedInputColumn>>();\nconst generateTableFilterValuesCached = (table: Table, tableName: string) => {\n\tif (filterMap.has(table)) return filterMap.get(table)!;\n\n\tconst columns = getTableColumns(table);\n\tconst columnEntries = Object.entries(columns);\n\n\tconst remapped = Object.fromEntries(\n\t\tcolumnEntries.map(([columnName, columnDescription]) => [\n\t\t\tcolumnName,\n\t\t\t{\n\t\t\t\ttype: generateColumnFilterValues(columnDescription, tableName, columnName),\n\t\t\t},\n\t\t]),\n\t);\n\n\tfilterMap.set(table, remapped);\n\n\treturn remapped;\n};\n\nconst fieldMap = new WeakMap<Object, Record<string, ConvertedColumn>>();\nconst generateTableSelectTypeFieldsCached = (table: Table, tableName: string): Record<string, ConvertedColumn> => {\n\tif (fieldMap.has(table)) return fieldMap.get(table)!;\n\n\tconst columns = getTableColumns(table);\n\tconst columnEntries = Object.entries(columns);\n\n\tconst remapped = Object.fromEntries(\n\t\tcolumnEntries.map(([columnName, columnDescription]) => [\n\t\t\tcolumnName,\n\t\t\tdrizzleColumnToGraphQLType(columnDescription, columnName, tableName),\n\t\t]),\n\t);\n\n\tfieldMap.set(table, remapped);\n\n\treturn remapped;\n};\n\nconst orderTypeMap = new WeakMap<Object, GraphQLInputObjectType>();\nconst generateTableOrderTypeCached = (table: Table, tableName: string) => {\n\tif (orderTypeMap.has(table)) return orderTypeMap.get(table)!;\n\n\tconst orderColumns = generateTableOrderCached(table);\n\tconst order = new GraphQLInputObjectType({\n\t\tname: `${capitalize(tableName)}OrderBy`,\n\t\tfields: orderColumns,\n\t});\n\n\torderTypeMap.set(table, order);\n\n\treturn order;\n};\n\nconst filterTypeMap = new WeakMap<Object, GraphQLInputObjectType>();\nconst generateTableFilterTypeCached = (table: Table, tableName: string) => {\n\tif (filterTypeMap.has(table)) return filterTypeMap.get(table)!;\n\n\tconst filterColumns = generateTableFilterValuesCached(table, tableName);\n\tconst filters: GraphQLInputObjectType = new GraphQLInputObjectType({\n\t\tname: `${capitalize(tableName)}Filters`,\n\t\tfields: {\n\t\t\t...filterColumns,\n\t\t\tOR: {\n\t\t\t\ttype: new GraphQLList(\n\t\t\t\t\tnew GraphQLNonNull(\n\t\t\t\t\t\tnew GraphQLInputObjectType({\n\t\t\t\t\t\t\tname: `${capitalize(tableName)}FiltersOr`,\n\t\t\t\t\t\t\tfields: filterColumns,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t});\n\n\tfilterTypeMap.set(table, filters);\n\n\treturn filters;\n};\n\nconst generateSelectFields = <TWithOrder extends boolean>(\n\ttables: Record<string, Table>,\n\ttableName: string,\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\ttypeName: string,\n\twithOrder: TWithOrder,\n\trelationsDepthLimit: number | undefined,\n\tcurrentDepth: number = 0,\n\tusedTables: Set<string> = new Set(),\n): SelectData<TWithOrder> => {\n\tconst relations = relationMap[tableName];\n\tconst relationEntries: [string, TableNamedRelations][] = relations ? Object.entries(relations) : [];\n\n\tconst table = tables[tableName]!;\n\n\tconst order = withOrder\n\t\t? generateTableOrderTypeCached(table, tableName)\n\t\t: undefined;\n\n\tconst filters = generateTableFilterTypeCached(table, tableName);\n\n\tconst tableFields = generateTableSelectTypeFieldsCached(table, tableName);\n\n\tif (\n\t\tusedTables.has(tableName) || (typeof relationsDepthLimit === 'number' && currentDepth >= relationsDepthLimit)\n\t\t|| !relationEntries.length\n\t) {\n\t\treturn {\n\t\t\torder,\n\t\t\tfilters,\n\t\t\ttableFields,\n\t\t\trelationFields: {},\n\t\t} as SelectData<TWithOrder>;\n\t}\n\n\tconst rawRelationFields: [string, ConvertedRelationColumnWithArgs][] = [];\n\tconst updatedUsedTables = new Set(usedTables).add(tableName);\n\tconst newDepth = currentDepth + 1;\n\n\tfor (const [relationName, { targetTableName, relation }] of relationEntries) {\n\t\tconst relTypeName = `${typeName}${capitalize(relationName)}Relation`;\n\t\tconst isOne = is(relation, One);\n\n\t\tconst relData = generateSelectFields(\n\t\t\ttables,\n\t\t\ttargetTableName,\n\t\t\trelationMap,\n\t\t\trelTypeName,\n\t\t\t!isOne,\n\t\t\trelationsDepthLimit,\n\t\t\tnewDepth,\n\t\t\tupdatedUsedTables,\n\t\t);\n\n\t\tconst relType = new GraphQLObjectType({\n\t\t\tname: relTypeName,\n\t\t\tfields: { ...relData.tableFields, ...relData.relationFields },\n\t\t});\n\n\t\tif (isOne) {\n\t\t\trawRelationFields.push([\n\t\t\t\trelationName,\n\t\t\t\t{\n\t\t\t\t\ttype: relType,\n\t\t\t\t\targs: {\n\t\t\t\t\t\twhere: { type: relData.filters },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t]);\n\n\t\t\tcontinue;\n\t\t}\n\n\t\trawRelationFields.push([\n\t\t\trelationName,\n\t\t\t{\n\t\t\t\ttype: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(relType))),\n\t\t\t\targs: {\n\t\t\t\t\twhere: { type: relData.filters },\n\t\t\t\t\torderBy: { type: relData.order! },\n\t\t\t\t\toffset: { type: GraphQLInt },\n\t\t\t\t\tlimit: { type: GraphQLInt },\n\t\t\t\t},\n\t\t\t},\n\t\t]);\n\t}\n\n\tconst relationFields = Object.fromEntries(rawRelationFields);\n\n\treturn { order, filters, tableFields, relationFields } as SelectData<TWithOrder>;\n};\n\nexport const generateTableTypes = <\n\tWithReturning extends boolean,\n>(\n\ttableName: string,\n\ttables: Record<string, Table>,\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\twithReturning: WithReturning,\n\trelationsDepthLimit: number | undefined,\n): GeneratedTableTypes<WithReturning> => {\n\tconst stylizedName = capitalize(tableName);\n\tconst { tableFields, relationFields, filters, order } = generateSelectFields(\n\t\ttables,\n\t\ttableName,\n\t\trelationMap,\n\t\tstylizedName,\n\t\ttrue,\n\t\trelationsDepthLimit,\n\t);\n\n\tconst table = tables[tableName]!;\n\tconst columns = getTableColumns(table);\n\tconst columnEntries = Object.entries(columns);\n\n\tconst insertFields = Object.fromEntries(\n\t\tcolumnEntries.map(([columnName, columnDescription]) => [\n\t\t\tcolumnName,\n\t\t\tdrizzleColumnToGraphQLType(columnDescription, columnName, tableName, false, true, true),\n\t\t]),\n\t);\n\n\tconst updateFields = Object.fromEntries(\n\t\tcolumnEntries.map(([columnName, columnDescription]) => [\n\t\t\tcolumnName,\n\t\t\tdrizzleColumnToGraphQLType(columnDescription, columnName, tableName, true, false, true),\n\t\t]),\n\t);\n\n\tconst insertInput = new GraphQLInputObjectType({\n\t\tname: `${stylizedName}InsertInput`,\n\t\tfields: insertFields,\n\t});\n\n\tconst selectSingleOutput = new GraphQLObjectType({\n\t\tname: `${stylizedName}SelectItem`,\n\t\tfields: { ...tableFields, ...relationFields },\n\t});\n\n\tconst selectArrOutput = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(selectSingleOutput)));\n\n\tconst singleTableItemOutput = withReturning\n\t\t? new GraphQLObjectType({\n\t\t\tname: `${stylizedName}Item`,\n\t\t\tfields: tableFields,\n\t\t})\n\t\t: undefined;\n\n\tconst arrTableItemOutput = withReturning\n\t\t? new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(singleTableItemOutput!)))\n\t\t: undefined;\n\n\tconst updateInput = new GraphQLInputObjectType({\n\t\tname: `${stylizedName}UpdateInput`,\n\t\tfields: updateFields,\n\t});\n\n\tconst inputs = {\n\t\tinsertInput,\n\t\tupdateInput,\n\t\ttableOrder: order,\n\t\ttableFilters: filters,\n\t};\n\n\tconst outputs = (\n\t\twithReturning\n\t\t\t? {\n\t\t\t\tselectSingleOutput,\n\t\t\t\tselectArrOutput,\n\t\t\t\tsingleTableItemOutput: singleTableItemOutput!,\n\t\t\t\tarrTableItemOutput: arrTableItemOutput!,\n\t\t\t}\n\t\t\t: {\n\t\t\t\tselectSingleOutput,\n\t\t\t\tselectArrOutput,\n\t\t\t}\n\t) as GeneratedTableTypesOutputs<WithReturning>;\n\n\treturn {\n\t\tinputs,\n\t\toutputs,\n\t};\n};\n\nexport const extractOrderBy = <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(\n\ttable: TTable,\n\torderArgs: TArgs,\n): SQL[] => {\n\tconst res = [] as SQL[];\n\n\tfor (\n\t\tconst [column, config] of Object.entries(orderArgs).sort(\n\t\t\t(a, b) => (b[1]?.priority ?? 0) - (a[1]?.priority ?? 0),\n\t\t)\n\t) {\n\t\tif (!config) continue;\n\t\tconst { direction } = config;\n\n\t\tres.push(direction === 'asc' ? asc(getTableColumns(table)[column]!) : desc(getTableColumns(table)[column]!));\n\t}\n\n\treturn res;\n};\n\nexport const extractFiltersColumn = <TColumn extends Column>(\n\tcolumn: TColumn,\n\tcolumnName: string,\n\toperators: FilterColumnOperators<TColumn>,\n): SQL | undefined => {\n\tif (!operators.OR?.length) delete operators.OR;\n\n\tconst entries = Object.entries(operators as FilterColumnOperatorsCore<TColumn>);\n\n\tif (operators.OR) {\n\t\tif (entries.length > 1) {\n\t\t\tthrow new GraphQLError(`WHERE ${columnName}: Cannot specify both fields and 'OR' in column operators!`);\n\t\t}\n\n\t\tconst variants = [] as SQL[];\n\n\t\tfor (const variant of operators.OR) {\n\t\t\tconst extracted = extractFiltersColumn(column, columnName, variant);\n\n\t\t\tif (extracted) variants.push(extracted);\n\t\t}\n\n\t\treturn variants.length ? (variants.length > 1 ? or(...variants) : variants[0]) : undefined;\n\t}\n\n\tconst variants = [] as SQL[];\n\tfor (const [operatorName, operatorValue] of entries) {\n\t\tif (operatorValue === null || operatorValue === false) continue;\n\n\t\tlet operator: ((...args: any[]) => SQL) | undefined;\n\t\tswitch (operatorName as keyof FilterColumnOperatorsCore<TColumn>) {\n\t\t\t// @ts-ignore\n\t\t\tcase 'eq':\n\t\t\t\toperator = operator ?? eq;\n\t\t\t// @ts-ignore\n\t\t\tcase 'ne':\n\t\t\t\toperator = operator ?? ne;\n\t\t\t// @ts-ignore\n\t\t\tcase 'gt':\n\t\t\t\toperator = operator ?? gt;\n\t\t\t// @ts-ignore\n\t\t\tcase 'gte':\n\t\t\t\toperator = operator ?? gte;\n\t\t\t// @ts-ignore\n\t\t\tcase 'lt':\n\t\t\t\toperator = operator ?? lt;\n\t\t\tcase 'lte':\n\t\t\t\toperator = operator ?? lte;\n\n\t\t\t\tconst singleValue = remapFromGraphQLCore(operatorValue, column, columnName);\n\t\t\t\tvariants.push(operator(column, singleValue));\n\n\t\t\t\tbreak;\n\n\t\t\t// @ts-ignore\n\t\t\tcase 'like':\n\t\t\t\toperator = operator ?? like;\n\t\t\t// @ts-ignore\n\t\t\tcase 'notLike':\n\t\t\t\toperator = operator ?? notLike;\n\t\t\t// @ts-ignore\n\t\t\tcase 'ilike':\n\t\t\t\toperator = operator ?? ilike;\n\t\t\tcase 'notIlike':\n\t\t\t\toperator = operator ?? notIlike;\n\n\t\t\t\tvariants.push(operator(column, operatorValue as string));\n\n\t\t\t\tbreak;\n\n\t\t\t// @ts-ignore\n\t\t\tcase 'inArray':\n\t\t\t\toperator = operator ?? inArray;\n\t\t\tcase 'notInArray':\n\t\t\t\toperator = operator ?? notInArray;\n\n\t\t\t\tif (!(operatorValue as any[]).length) {\n\t\t\t\t\tthrow new GraphQLError(\n\t\t\t\t\t\t`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst arrayValue = (operatorValue as any[]).map((val) => remapFromGraphQLCore(val, column, columnName));\n\n\t\t\t\tvariants.push(operator(column, arrayValue));\n\t\t\t\tbreak;\n\n\t\t\t// @ts-ignore\n\t\t\tcase 'isNull':\n\t\t\t\toperator = operator ?? isNull;\n\t\t\tcase 'isNotNull':\n\t\t\t\toperator = operator ?? isNotNull;\n\n\t\t\t\tvariants.push(operator(column));\n\t\t}\n\t}\n\n\treturn variants.length ? (variants.length > 1 ? and(...variants) : variants[0]) : undefined;\n};\n\nexport const extractFilters = <TTable extends Table>(\n\ttable: TTable,\n\ttableName: string,\n\tfilters: Filters<TTable>,\n): SQL | undefined => {\n\tif (!filters.OR?.length) delete filters.OR;\n\n\tconst entries = Object.entries(filters as FiltersCore<TTable>);\n\tif (!entries.length) return;\n\n\tif (filters.OR) {\n\t\tif (entries.length > 1) {\n\t\t\tthrow new GraphQLError(`WHERE ${tableName}: Cannot specify both fields and 'OR' in table filters!`);\n\t\t}\n\n\t\tconst variants = [] as SQL[];\n\n\t\tfor (const variant of filters.OR) {\n\t\t\tconst extracted = extractFilters(table, tableName, variant);\n\t\t\tif (extracted) variants.push(extracted);\n\t\t}\n\n\t\treturn variants.length ? (variants.length > 1 ? or(...variants) : variants[0]) : undefined;\n\t}\n\n\tconst variants = [] as SQL[];\n\tfor (const [columnName, operators] of entries) {\n\t\tif (operators === null) continue;\n\n\t\tconst column = getTableColumns(table)[columnName]!;\n\t\tvariants.push(extractFiltersColumn(column, columnName, operators)!);\n\t}\n\n\treturn variants.length ? (variants.length > 1 ? and(...variants) : variants[0]) : undefined;\n};\n\nconst extractRelationsParamsInner = (\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\ttables: Record<string, Table>,\n\ttableName: string,\n\ttypeName: string,\n\toriginField: ResolveTree,\n\tisInitial: boolean = false,\n) => {\n\tconst relations = relationMap[tableName];\n\tif (!relations) return undefined;\n\n\tconst baseField = Object.entries(originField.fieldsByTypeName).find(([key, value]) => key === typeName)?.[1];\n\tif (!baseField) return undefined;\n\n\tconst args: Record<string, Partial<ProcessedTableSelectArgs>> = {};\n\n\tfor (const [relName, { targetTableName, relation }] of Object.entries(relations)) {\n\t\tconst relTypeName = `${isInitial ? capitalize(tableName) : typeName}${capitalize(relName)}Relation`;\n\t\tconst relFieldSelection = Object.values(baseField).find((field) =>\n\t\t\tfield.name === relName\n\t\t)?.fieldsByTypeName[relTypeName];\n\t\tif (!relFieldSelection) continue;\n\n\t\tconst columns = extractSelectedColumnsFromTree(relFieldSelection, tables[targetTableName]!);\n\n\t\tconst thisRecord: Partial<ProcessedTableSelectArgs> = {};\n\t\tthisRecord.columns = columns;\n\n\t\tconst relationField = Object.values(baseField).find((e) => e.name === relName);\n\t\tconst relationArgs: Partial<TableSelectArgs> | undefined = relationField?.args;\n\n\t\tconst orderBy = relationArgs?.orderBy ? extractOrderBy(tables[targetTableName]!, relationArgs.orderBy!) : undefined;\n\t\tconst where = relationArgs?.where\n\t\t\t? extractFilters(tables[targetTableName]!, relName, relationArgs?.where)\n\t\t\t: undefined;\n\t\tconst offset = relationArgs?.offset ?? undefined;\n\t\tconst limit = relationArgs?.limit ?? undefined;\n\n\t\tthisRecord.orderBy = orderBy;\n\t\tthisRecord.where = where;\n\t\tthisRecord.offset = offset;\n\t\tthisRecord.limit = limit;\n\n\t\tconst relWith = relationField\n\t\t\t? extractRelationsParamsInner(relationMap, tables, targetTableName, relTypeName, relationField)\n\t\t\t: undefined;\n\t\tthisRecord.with = relWith;\n\n\t\targs[relName] = thisRecord;\n\t}\n\n\treturn args;\n};\n\nexport const extractRelationsParams = (\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\ttables: Record<string, Table>,\n\ttableName: string,\n\tinfo: ResolveTree | undefined,\n\ttypeName: string,\n): Record<string, Partial<ProcessedTableSelectArgs>> | undefined => {\n\tif (!info) return undefined;\n\n\treturn extractRelationsParamsInner(relationMap, tables, tableName, typeName, info, true);\n};\n","export const uncapitalize = <T extends string>(input: T) =>\n\t(input.length\n\t\t? `${input[0]!.toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ''}`\n\t\t: input) as Uncapitalize<T>;\n\nexport const capitalize = <T extends string>(input: T) =>\n\t(input.length\n\t\t? `${input[0]!.toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ''}`\n\t\t: input) as Capitalize<T>;\n","import { type Column, getTableColumns, type Table } from 'drizzle-orm';\nimport { GraphQLError } from 'graphql';\nimport { TableNamedRelations } from '../builders';\n\nexport const remapToGraphQLCore = (\n\tkey: string,\n\tvalue: any,\n\ttableName: string,\n\tcolumn: Column,\n\trelationMap?: Record<string, Record<string, TableNamedRelations>>,\n): any => {\n\tif (value instanceof Date) return value.toISOString();\n\n\tif (value instanceof Buffer) return Array.from(value);\n\n\tif (typeof value === 'bigint') return value.toString();\n\n\tif (Array.isArray(value)) {\n\t\tconst relations = relationMap?.[tableName];\n\t\tif (relations?.[key]) {\n\t\t\treturn remapToGraphQLArrayOutput(\n\t\t\t\tvalue,\n\t\t\t\trelations[key]!.targetTableName,\n\t\t\t\trelations[key]!.relation.referencedTable,\n\t\t\t\trelationMap,\n\t\t\t);\n\t\t}\n\t\tif (column.columnType === 'PgGeometry' || column.columnType === 'PgVector') return value;\n\n\t\treturn value.map((arrVal) => remapToGraphQLCore(key, arrVal, tableName, column, relationMap));\n\t}\n\n\tif (typeof value === 'object') {\n\t\tconst relations = relationMap?.[tableName];\n\t\tif (relations?.[key]) {\n\t\t\treturn remapToGraphQLSingleOutput(\n\t\t\t\tvalue,\n\t\t\t\trelations[key]!.targetTableName,\n\t\t\t\trelations[key]!.relation.referencedTable,\n\t\t\t\trelationMap,\n\t\t\t);\n\t\t}\n\t\tif (column.columnType === 'PgGeometryObject') return value;\n\n\t\treturn JSON.stringify(value);\n\t}\n\n\treturn value;\n};\n\nexport const remapToGraphQLSingleOutput = (\n\tqueryOutput: Record<string, any>,\n\ttableName: string,\n\ttable: Table,\n\trelationMap?: Record<string, Record<string, TableNamedRelations>>,\n) => {\n\tfor (const [key, value] of Object.entries(queryOutput)) {\n\t\tif (value === undefined || value === null) {\n\t\t\tdelete queryOutput[key];\n\t\t} else {\n\t\t\tqueryOutput[key] = remapToGraphQLCore(key, value, tableName, table[key as keyof Table]! as Column, relationMap);\n\t\t}\n\t}\n\n\treturn queryOutput;\n};\n\nexport const remapToGraphQLArrayOutput = (\n\tqueryOutput: Record<string, any>[],\n\ttableName: string,\n\ttable: Table,\n\trelationMap?: Record<string, Record<string, TableNamedRelations>>,\n) => {\n\tfor (const entry of queryOutput) {\n\t\tremapToGraphQLSingleOutput(entry, tableName, table, relationMap);\n\t}\n\n\treturn queryOutput;\n};\n\nexport const remapFromGraphQLCore = (value: any, column: Column, columnName: string) => {\n\tswitch (column.dataType) {\n\t\tcase 'date': {\n\t\t\tconst formatted = new Date(value);\n\t\t\tif (Number.isNaN(formatted.getTime())) throw new GraphQLError(`Field '${columnName}' is not a valid date!`);\n\n\t\t\treturn formatted;\n\t\t}\n\n\t\tcase 'buffer': {\n\t\t\tif (!Array.isArray(value)) {\n\t\t\t\tthrow new GraphQLError(`Field '${columnName}' is not an array!`);\n\t\t\t}\n\n\t\t\treturn Buffer.from(value);\n\t\t}\n\n\t\tcase 'json': {\n\t\t\tif (column.columnType === 'PgGeometryObject') return value;\n\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(value);\n\t\t\t} catch (e) {\n\t\t\t\tthrow new GraphQLError(\n\t\t\t\t\t`Invalid JSON in field '${columnName}':\\n${e instanceof Error ? e.message : 'Unknown error'}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tcase 'array': {\n\t\t\tif (!Array.isArray(value)) {\n\t\t\t\tthrow new GraphQLError(`Field '${columnName}' is not an array!`);\n\t\t\t}\n\n\t\t\tif (column.columnType === 'PgGeometry' && value.length !== 2) {\n\t\t\t\tthrow new GraphQLError(\n\t\t\t\t\t`Invalid float tuple in field '${columnName}': expected array with length of 2, received ${value.length}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn value;\n\t\t}\n\n\t\tcase 'bigint': {\n\t\t\ttry {\n\t\t\t\treturn BigInt(value);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new GraphQLError(`Field '${columnName}' is not a BigInt!`);\n\t\t\t}\n\t\t}\n\n\t\tdefault: {\n\t\t\treturn value;\n\t\t}\n\t}\n};\n\nexport const remapFromGraphQLSingleInput = (queryInput: Record<string, any>, table: Table) => {\n\tfor (const [key, value] of Object.entries(queryInput)) {\n\t\tif (value === undefined) {\n\t\t\tdelete queryInput[key];\n\t\t} else {\n\t\t\tconst column = getTableColumns(table)[key];\n\t\t\tif (!column) throw new GraphQLError(`Unknown column: ${key}`);\n\n\t\t\tif (value === null && column.notNull) {\n\t\t\t\tdelete queryInput[key];\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tqueryInput[key] = remapFromGraphQLCore(value, column, key);\n\t\t}\n\t}\n\n\treturn queryInput;\n};\n\nexport const remapFromGraphQLArrayInput = (queryInput: Record<string, any>[], table: Table) => {\n\tfor (const entry of queryInput) remapFromGraphQLSingleInput(entry, table);\n\n\treturn queryInput;\n};\n","import { is } from 'drizzle-orm';\nimport { MySqlInt, MySqlSerial } from 'drizzle-orm/mysql-core';\nimport { PgInteger, PgSerial } from 'drizzle-orm/pg-core';\nimport { SQLiteInteger } from 'drizzle-orm/sqlite-core';\nimport {\n\tGraphQLBoolean,\n\tGraphQLEnumType,\n\tGraphQLFloat,\n\tGraphQLInputObjectType,\n\tGraphQLInt,\n\tGraphQLList,\n\tGraphQLNonNull,\n\tGraphQLObjectType,\n\tGraphQLScalarType,\n\tGraphQLString,\n} from 'graphql';\n\nimport type { Column } from 'drizzle-orm';\nimport type { PgArray } from 'drizzle-orm/pg-core';\nimport { capitalize } from '../case-ops';\nimport type { ConvertedColumn } from './types';\n\nconst allowedNameChars = /^[a-zA-Z0-9_]+$/;\n\nconst enumMap = new WeakMap<Object, GraphQLEnumType>();\nconst generateEnumCached = (column: Column, columnName: string, tableName: string): GraphQLEnumType => {\n\tif (enumMap.has(column)) return enumMap.get(column)!;\n\n\tconst gqlEnum = new GraphQLEnumType({\n\t\tname: `${capitalize(tableName)}${capitalize(columnName)}Enum`,\n\t\tvalues: Object.fromEntries(column.enumValues!.map((e, index) => [allowedNameChars.test(e) ? e : `Option${index}`, {\n\t\t\tvalue: e,\n\t\t\tdescription: `Value: ${e}`,\n\t\t}])),\n\t});\n\n\tenumMap.set(column, gqlEnum);\n\n\treturn gqlEnum;\n};\n\nconst geoXyType = new GraphQLObjectType({\n\tname: 'PgGeometryObject',\n\tfields: {\n\t\tx: { type: GraphQLFloat },\n\t\ty: { type: GraphQLFloat },\n\t},\n});\n\nconst geoXyInputType = new GraphQLInputObjectType({\n\tname: 'PgGeometryObjectInput',\n\tfields: {\n\t\tx: { type: GraphQLFloat },\n\t\ty: { type: GraphQLFloat },\n\t},\n});\n\nconst columnToGraphQLCore = (\n\tcolumn: Column,\n\tcolumnName: string,\n\ttableName: string,\n\tisInput: boolean,\n): ConvertedColumn<boolean> => {\n\tswitch (column.dataType) {\n\t\tcase 'boolean':\n\t\t\treturn { type: GraphQLBoolean, description: 'Boolean' };\n\t\tcase 'json':\n\t\t\treturn column.columnType === 'PgGeometryObject'\n\t\t\t\t? {\n\t\t\t\t\ttype: isInput ? geoXyInputType : geoXyType,\n\t\t\t\t\tdescription: 'Geometry points XY',\n\t\t\t\t}\n\t\t\t\t: { type: GraphQLString, description: 'JSON' };\n\t\tcase 'date':\n\t\t\treturn { type: GraphQLString, description: 'Date' };\n\t\tcase 'string':\n\t\t\tif (column.enumValues?.length) return { type: generateEnumCached(column, columnName, tableName) };\n\n\t\t\treturn { type: GraphQLString, description: 'String' };\n\t\tcase 'bigint':\n\t\t\treturn { type: GraphQLString, description: 'BigInt' };\n\t\tcase 'number':\n\t\t\treturn is(column, PgInteger)\n\t\t\t\t\t|| is(column, PgSerial)\n\t\t\t\t\t|| is(column, MySqlInt)\n\t\t\t\t\t|| is(column, MySqlSerial)\n\t\t\t\t\t|| is(column, SQLiteInteger)\n\t\t\t\t? { type: GraphQLInt, description: 'Integer' }\n\t\t\t\t: { type: GraphQLFloat, description: 'Float' };\n\t\tcase 'buffer':\n\t\t\treturn { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)), description: 'Buffer' };\n\t\tcase 'array': {\n\t\t\tif (column.columnType === 'PgVector') {\n\t\t\t\treturn {\n\t\t\t\t\ttype: new GraphQLList(new GraphQLNonNull(GraphQLFloat)),\n\t\t\t\t\tdescription: 'Array<Float>',\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (column.columnType === 'PgGeometry') {\n\t\t\t\treturn {\n\t\t\t\t\ttype: new GraphQLList(new GraphQLNonNull(GraphQLFloat)),\n\t\t\t\t\tdescription: 'Tuple<[Float, Float]>',\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst innerType = columnToGraphQLCore(\n\t\t\t\t(column as Column as PgArray<any, any>).baseColumn,\n\t\t\t\tcolumnName,\n\t\t\t\ttableName,\n\t\t\t\tisInput,\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\ttype: new GraphQLList(new GraphQLNonNull(innerType.type as GraphQLScalarType)),\n\t\t\t\tdescription: `Array<${innerType.description}>`,\n\t\t\t};\n\t\t}\n\t\tcase 'custom':\n\t\tdefault:\n\t\t\tthrow new Error(`Drizzle-GraphQL Error: Type ${column.dataType} is not implemented!`);\n\t}\n};\n\nexport const drizzleColumnToGraphQLType = <TColumn extends Column, TIsInput extends boolean>(\n\tcolumn: TColumn,\n\tcolumnName: string,\n\ttableName: string,\n\tforceNullable = false,\n\tdefaultIsNullable = false,\n\tisInput: TIsInput = false as TIsInput,\n): ConvertedColumn<TIsInput> => {\n\tconst typeDesc = columnToGraphQLCore(column, columnName, tableName, isInput);\n\tconst noDesc = ['string', 'boolean', 'number'];\n\tif (noDesc.find((e) => e === column.dataType)) delete typeDesc.description;\n\n\tif (forceNullable) return typeDesc as ConvertedColumn<TIsInput>;\n\tif (column.notNull && !(defaultIsNullable && (column.hasDefault || column.defaultFn))) {\n\t\treturn {\n\t\t\ttype: new GraphQLNonNull(typeDesc.type),\n\t\t\tdescription: typeDesc.description,\n\t\t} as ConvertedColumn<TIsInput>;\n\t}\n\n\treturn typeDesc as ConvertedColumn<TIsInput>;\n};\n\nexport * from './types';\n","import { createTableRelationsHelpers, is, Relation, Relations, Table } from 'drizzle-orm';\nimport { PgColumn, PgDatabase, PgTable } from 'drizzle-orm/pg-core';\nimport {\n\tGraphQLError,\n\tGraphQLInputObjectType,\n\tGraphQLInt,\n\tGraphQLList,\n\tGraphQLNonNull,\n\tGraphQLObjectType,\n} from 'graphql';\n\nimport {\n\textractFilters,\n\textractOrderBy,\n\textractRelationsParams,\n\textractSelectedColumnsFromTree,\n\textractSelectedColumnsFromTreeSQLFormat,\n\tgenerateTableTypes,\n} from '@/util/builders/common';\nimport { capitalize, uncapitalize } from '@/util/case-ops';\nimport {\n\tremapFromGraphQLArrayInput,\n\tremapFromGraphQLSingleInput,\n\tremapToGraphQLArrayOutput,\n\tremapToGraphQLSingleOutput,\n} from '@/util/data-mappers';\nimport { parseResolveInfo } from 'graphql-parse-resolve-info';\n\nimport type { GeneratedEntities } from '@/types';\nimport type { RelationalQueryBuilder } from 'drizzle-orm/mysql-core/query-builders/query';\nimport type { GraphQLFieldConfig, GraphQLFieldConfigArgumentMap, ThunkObjMap } from 'graphql';\nimport type { ResolveTree } from 'graphql-parse-resolve-info';\nimport type { CreatedResolver, Filters, TableNamedRelations, TableSelectArgs } from './types';\n\nconst generateSelectArray = (\n\tdb: PgDatabase<any, any, any>,\n\ttableName: string,\n\ttables: Record<string, Table>,\n\trelationMap: Record<string, Record<string, TableNamedRelations>>,\n\torderArgs: GraphQLInputObjectType,\n\tfilterArgs: GraphQLInputObjectType,\n): CreatedResolver => {\n\tconst queryName = `${uncapitalize(tableName)}`;\n\tconst queryBase = db.query[tableName as keyof ty