@drizzle-graphql-suite/schema 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,1812 @@
1
+ // src/index.ts
2
+ import { createTableRelationsHelpers as createTableRelationsHelpers2, extractTablesRelationalConfig } from "drizzle-orm";
3
+
4
+ // src/schema-builder.ts
5
+ import {
6
+ and,
7
+ asc,
8
+ count,
9
+ createTableRelationsHelpers,
10
+ desc,
11
+ eq,
12
+ exists,
13
+ getTableColumns as getTableColumns2,
14
+ getTableName,
15
+ gt,
16
+ gte,
17
+ ilike,
18
+ inArray,
19
+ is as is3,
20
+ isNotNull,
21
+ isNull,
22
+ like,
23
+ lt,
24
+ lte,
25
+ ne,
26
+ normalizeRelation,
27
+ not,
28
+ notIlike,
29
+ notInArray,
30
+ notLike,
31
+ One,
32
+ or,
33
+ Relations,
34
+ sql
35
+ } from "drizzle-orm";
36
+ import { PgTable as PgTable2 } from "drizzle-orm/pg-core";
37
+ import {
38
+ GraphQLBoolean as GraphQLBoolean2,
39
+ GraphQLEnumType as GraphQLEnumType2,
40
+ GraphQLError as GraphQLError2,
41
+ GraphQLInputObjectType as GraphQLInputObjectType2,
42
+ GraphQLInt as GraphQLInt2,
43
+ GraphQLList as GraphQLList2,
44
+ GraphQLNonNull as GraphQLNonNull2,
45
+ GraphQLObjectType as GraphQLObjectType2,
46
+ GraphQLSchema,
47
+ GraphQLString as GraphQLString2,
48
+ printSchema
49
+ } from "graphql";
50
+ import { parseResolveInfo } from "graphql-parse-resolve-info";
51
+
52
+ // src/adapters/pg.ts
53
+ import { is } from "drizzle-orm";
54
+ import { PgTable } from "drizzle-orm/pg-core";
55
+
56
+ class PgAdapter {
57
+ supportsReturning = true;
58
+ isTable(value) {
59
+ return is(value, PgTable);
60
+ }
61
+ async executeInsert(db, table, values, returningColumns) {
62
+ let query = db.insert(table).values(values);
63
+ if (returningColumns) {
64
+ query = query.returning(returningColumns);
65
+ }
66
+ query = query.onConflictDoNothing();
67
+ return await query;
68
+ }
69
+ async executeUpdate(db, table, set, where, returningColumns) {
70
+ let query = db.update(table).set(set);
71
+ if (where) {
72
+ query = query.where(where);
73
+ }
74
+ if (returningColumns) {
75
+ query = query.returning(returningColumns);
76
+ }
77
+ return await query;
78
+ }
79
+ async executeDelete(db, table, where, returningColumns) {
80
+ let query = db.delete(table);
81
+ if (where) {
82
+ query = query.where(where);
83
+ }
84
+ if (returningColumns) {
85
+ query = query.returning(returningColumns);
86
+ }
87
+ return await query;
88
+ }
89
+ }
90
+
91
+ // src/case-ops.ts
92
+ var uncapitalize = (input) => input.length ? `${input[0]?.toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
93
+ var capitalize = (input) => input.length ? `${input[0]?.toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
94
+
95
+ // src/data-mappers.ts
96
+ import { getTableColumns } from "drizzle-orm";
97
+ import { GraphQLError } from "graphql";
98
+ var remapToGraphQLCore = (key, value, tableName, column, relationMap) => {
99
+ if (value instanceof Date)
100
+ return value.toISOString();
101
+ if (value instanceof Buffer)
102
+ return Array.from(value);
103
+ if (typeof value === "bigint")
104
+ return value.toString();
105
+ if (Array.isArray(value)) {
106
+ const rel = relationMap?.[tableName]?.[key];
107
+ if (rel) {
108
+ return remapToGraphQLArrayOutput(value, rel.targetTableName, rel.relation.referencedTable, relationMap);
109
+ }
110
+ if (column.columnType === "PgGeometry" || column.columnType === "PgVector")
111
+ return value;
112
+ return value.map((arrVal) => remapToGraphQLCore(key, arrVal, tableName, column, relationMap));
113
+ }
114
+ if (typeof value === "object") {
115
+ const rel = relationMap?.[tableName]?.[key];
116
+ if (rel) {
117
+ return remapToGraphQLSingleOutput(value, rel.targetTableName, rel.relation.referencedTable, relationMap);
118
+ }
119
+ if (column.columnType === "PgGeometryObject")
120
+ return value;
121
+ if (column.dataType === "json")
122
+ return value;
123
+ return JSON.stringify(value);
124
+ }
125
+ return value;
126
+ };
127
+ var remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap) => {
128
+ for (const [key, value] of Object.entries(queryOutput)) {
129
+ if (value === undefined || value === null) {
130
+ delete queryOutput[key];
131
+ } else {
132
+ queryOutput[key] = remapToGraphQLCore(key, value, tableName, table[key], relationMap);
133
+ }
134
+ }
135
+ return queryOutput;
136
+ };
137
+ var remapToGraphQLArrayOutput = (queryOutput, tableName, table, relationMap) => {
138
+ for (const entry of queryOutput) {
139
+ remapToGraphQLSingleOutput(entry, tableName, table, relationMap);
140
+ }
141
+ return queryOutput;
142
+ };
143
+ var remapFromGraphQLCore = (value, column, columnName) => {
144
+ switch (column.dataType) {
145
+ case "date": {
146
+ const formatted = new Date(value);
147
+ if (Number.isNaN(formatted.getTime()))
148
+ throw new GraphQLError(`Field '${columnName}' is not a valid date!`);
149
+ return formatted;
150
+ }
151
+ case "buffer": {
152
+ if (!Array.isArray(value)) {
153
+ throw new GraphQLError(`Field '${columnName}' is not an array!`);
154
+ }
155
+ return Buffer.from(value);
156
+ }
157
+ case "json": {
158
+ if (column.columnType === "PgGeometryObject")
159
+ return value;
160
+ return value;
161
+ }
162
+ case "array": {
163
+ if (!Array.isArray(value)) {
164
+ throw new GraphQLError(`Field '${columnName}' is not an array!`);
165
+ }
166
+ if (column.columnType === "PgGeometry" && value.length !== 2) {
167
+ throw new GraphQLError(`Invalid float tuple in field '${columnName}': expected array with length of 2, received ${value.length}`);
168
+ }
169
+ return value;
170
+ }
171
+ case "bigint": {
172
+ try {
173
+ return BigInt(value);
174
+ } catch {
175
+ throw new GraphQLError(`Field '${columnName}' is not a BigInt!`);
176
+ }
177
+ }
178
+ default: {
179
+ return value;
180
+ }
181
+ }
182
+ };
183
+ var remapFromGraphQLSingleInput = (queryInput, table) => {
184
+ for (const [key, value] of Object.entries(queryInput)) {
185
+ if (value === undefined) {
186
+ delete queryInput[key];
187
+ } else {
188
+ const column = getTableColumns(table)[key];
189
+ if (!column)
190
+ throw new GraphQLError(`Unknown column: ${key}`);
191
+ if (value === null && column.notNull) {
192
+ delete queryInput[key];
193
+ continue;
194
+ }
195
+ queryInput[key] = remapFromGraphQLCore(value, column, key);
196
+ }
197
+ }
198
+ return queryInput;
199
+ };
200
+ var remapFromGraphQLArrayInput = (queryInput, table) => {
201
+ for (const entry of queryInput)
202
+ remapFromGraphQLSingleInput(entry, table);
203
+ return queryInput;
204
+ };
205
+
206
+ // src/graphql/type-builder.ts
207
+ import { is as is2 } from "drizzle-orm";
208
+ import {
209
+ PgBigInt53,
210
+ PgBigSerial53,
211
+ PgInteger,
212
+ PgSerial,
213
+ PgSmallInt,
214
+ PgSmallSerial
215
+ } from "drizzle-orm/pg-core";
216
+ import {
217
+ GraphQLBoolean,
218
+ GraphQLEnumType,
219
+ GraphQLFloat,
220
+ GraphQLInputObjectType,
221
+ GraphQLInt,
222
+ GraphQLList,
223
+ GraphQLNonNull,
224
+ GraphQLObjectType,
225
+ GraphQLString
226
+ } from "graphql";
227
+
228
+ // src/graphql/scalars.ts
229
+ import { GraphQLScalarType, Kind } from "graphql";
230
+ var GraphQLJSON = new GraphQLScalarType({
231
+ name: "JSON",
232
+ description: "The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).",
233
+ serialize(value) {
234
+ return value;
235
+ },
236
+ parseValue(value) {
237
+ return value;
238
+ },
239
+ parseLiteral(ast) {
240
+ switch (ast.kind) {
241
+ case Kind.STRING:
242
+ case Kind.BOOLEAN:
243
+ return ast.value;
244
+ case Kind.INT:
245
+ case Kind.FLOAT:
246
+ return parseFloat(ast.value);
247
+ case Kind.OBJECT: {
248
+ const value = Object.create(null);
249
+ ast.fields.forEach((field) => {
250
+ value[field.name.value] = GraphQLJSON.parseLiteral(field.value);
251
+ });
252
+ return value;
253
+ }
254
+ case Kind.LIST:
255
+ return ast.values.map((n) => GraphQLJSON.parseLiteral(n));
256
+ case Kind.NULL:
257
+ return null;
258
+ default:
259
+ return;
260
+ }
261
+ }
262
+ });
263
+
264
+ // src/graphql/type-builder.ts
265
+ var allowedNameChars = /^[a-zA-Z0-9_]+$/;
266
+ var enumMap = new WeakMap;
267
+ var generateEnumCached = (column, columnName, tableName) => {
268
+ const cached = enumMap.get(column);
269
+ if (cached)
270
+ return cached;
271
+ const gqlEnum = new GraphQLEnumType({
272
+ name: `${capitalize(tableName)}${capitalize(columnName)}Enum`,
273
+ values: Object.fromEntries((column.enumValues ?? []).map((e, index) => {
274
+ const enumName = e.replace(/-/g, "_");
275
+ const finalName = allowedNameChars.test(enumName) ? enumName : `Option${index}`;
276
+ return [
277
+ finalName,
278
+ {
279
+ value: e,
280
+ description: `Value: ${e}`
281
+ }
282
+ ];
283
+ }))
284
+ });
285
+ enumMap.set(column, gqlEnum);
286
+ return gqlEnum;
287
+ };
288
+ var geoXyType = new GraphQLObjectType({
289
+ name: "PgGeometryObject",
290
+ fields: {
291
+ x: { type: GraphQLFloat },
292
+ y: { type: GraphQLFloat }
293
+ }
294
+ });
295
+ var geoXyInputType = new GraphQLInputObjectType({
296
+ name: "PgGeometryObjectInput",
297
+ fields: {
298
+ x: { type: GraphQLFloat },
299
+ y: { type: GraphQLFloat }
300
+ }
301
+ });
302
+ var columnToGraphQLCore = (column, columnName, tableName, isInput) => {
303
+ switch (column.dataType) {
304
+ case "boolean":
305
+ return { type: GraphQLBoolean, description: "Boolean" };
306
+ case "json":
307
+ return column.columnType === "PgGeometryObject" ? {
308
+ type: isInput ? geoXyInputType : geoXyType,
309
+ description: "Geometry points XY"
310
+ } : { type: GraphQLJSON, description: "JSON" };
311
+ case "date":
312
+ return { type: GraphQLString, description: "Date" };
313
+ case "string":
314
+ if (column.enumValues?.length) {
315
+ return { type: generateEnumCached(column, columnName, tableName) };
316
+ }
317
+ return { type: GraphQLString, description: "String" };
318
+ case "bigint":
319
+ return { type: GraphQLString, description: "BigInt" };
320
+ case "number":
321
+ return is2(column, PgInteger) || is2(column, PgSmallInt) || is2(column, PgBigInt53) || is2(column, PgSerial) || is2(column, PgSmallSerial) || is2(column, PgBigSerial53) ? { type: GraphQLInt, description: "Integer" } : { type: GraphQLFloat, description: "Float" };
322
+ case "buffer":
323
+ return {
324
+ type: new GraphQLList(new GraphQLNonNull(GraphQLInt)),
325
+ description: "Buffer"
326
+ };
327
+ case "array": {
328
+ if (column.columnType === "PgVector") {
329
+ return {
330
+ type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)),
331
+ description: "Array<Float>"
332
+ };
333
+ }
334
+ if (column.columnType === "PgGeometry") {
335
+ return {
336
+ type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)),
337
+ description: "Tuple<[Float, Float]>"
338
+ };
339
+ }
340
+ const innerType = columnToGraphQLCore(column.baseColumn, columnName, tableName, isInput);
341
+ return {
342
+ type: new GraphQLList(new GraphQLNonNull(innerType.type)),
343
+ description: `Array<${innerType.description}>`
344
+ };
345
+ }
346
+ default:
347
+ throw new Error(`Drizzle-GraphQL Error: Type ${column.dataType} is not implemented!`);
348
+ }
349
+ };
350
+ var drizzleColumnToGraphQLType = (column, columnName, tableName, forceNullable = false, defaultIsNullable = false, isInput = false) => {
351
+ const typeDesc = columnToGraphQLCore(column, columnName, tableName, isInput);
352
+ const noDesc = ["string", "boolean", "number"];
353
+ if (noDesc.find((e) => e === column.dataType))
354
+ delete typeDesc.description;
355
+ if (forceNullable)
356
+ return typeDesc;
357
+ if (column.notNull && !(defaultIsNullable && (column.hasDefault || column.defaultFn))) {
358
+ return {
359
+ type: new GraphQLNonNull(typeDesc.type),
360
+ description: typeDesc.description
361
+ };
362
+ }
363
+ return typeDesc;
364
+ };
365
+
366
+ // src/schema-builder.ts
367
+ class SchemaBuilder {
368
+ db;
369
+ tables;
370
+ relationMap;
371
+ relationalSchema;
372
+ tableNamesMap;
373
+ config;
374
+ hooks;
375
+ adapter;
376
+ suffixes;
377
+ limitRelationDepth;
378
+ limitSelfRelationDepth;
379
+ excludedTables;
380
+ tableOperations;
381
+ pruneRelations;
382
+ filterTypes = new Map;
383
+ orderByTypes = new Map;
384
+ filterValueTypes = new Map;
385
+ selectFieldTypes = new Map;
386
+ innerOrder;
387
+ constructor(db, config) {
388
+ this.db = db;
389
+ this.config = config ?? {};
390
+ this.hooks = config?.hooks ?? {};
391
+ this.adapter = new PgAdapter;
392
+ this.suffixes = {
393
+ list: config?.suffixes?.list ?? "",
394
+ single: config?.suffixes?.single ?? "Single"
395
+ };
396
+ this.limitRelationDepth = config?.limitRelationDepth ?? 3;
397
+ this.limitSelfRelationDepth = config?.limitSelfRelationDepth ?? 1;
398
+ this.pruneRelations = new Map(Object.entries(config?.pruneRelations ?? {}));
399
+ const schema = db._.fullSchema;
400
+ if (!schema) {
401
+ throw new Error("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!");
402
+ }
403
+ const dbConfig = db._;
404
+ this.relationalSchema = dbConfig.schema;
405
+ this.tableNamesMap = dbConfig.tableNamesMap;
406
+ if (typeof this.limitRelationDepth === "number") {
407
+ if (this.limitRelationDepth < 0 || this.limitRelationDepth !== ~~this.limitRelationDepth) {
408
+ throw new Error("Drizzle-GraphQL Error: config.limitRelationDepth is supposed to be nonnegative integer or undefined!");
409
+ }
410
+ }
411
+ if (this.limitSelfRelationDepth < 1 || this.limitSelfRelationDepth !== ~~this.limitSelfRelationDepth) {
412
+ throw new Error("Drizzle-GraphQL Error: config.limitSelfRelationDepth must be a positive integer!");
413
+ }
414
+ if (this.suffixes.list === this.suffixes.single) {
415
+ throw new Error("Drizzle-GraphQL Error: List and single query suffixes cannot be the same. This would create conflicting GraphQL field names.");
416
+ }
417
+ this.innerOrder = new GraphQLInputObjectType2({
418
+ name: "InnerOrder",
419
+ fields: {
420
+ direction: {
421
+ type: new GraphQLNonNull2(new GraphQLEnumType2({
422
+ name: "OrderDirection",
423
+ description: "Order by direction",
424
+ values: {
425
+ asc: { value: "asc", description: "Ascending order" },
426
+ desc: { value: "desc", description: "Descending order" }
427
+ }
428
+ }))
429
+ },
430
+ priority: {
431
+ type: new GraphQLNonNull2(GraphQLInt2),
432
+ description: "Priority of current field"
433
+ }
434
+ }
435
+ });
436
+ this.tables = this.extractTables(schema);
437
+ this.excludedTables = new Set(config?.tables?.exclude ?? []);
438
+ this.tableOperations = config?.tables?.config ?? {};
439
+ for (const tableName of this.excludedTables) {
440
+ delete this.tables[tableName];
441
+ }
442
+ this.relationMap = this.buildRelationMap(schema);
443
+ }
444
+ toFieldMap(tableFields, relationFields) {
445
+ return { ...tableFields, ...relationFields };
446
+ }
447
+ buildEntities() {
448
+ const queries = {};
449
+ const mutations = {};
450
+ const inputs = {};
451
+ const outputs = {};
452
+ for (const tableName of Object.keys(this.tables)) {
453
+ const tableOps = this.tableOperations[tableName];
454
+ const generateQueries = tableOps?.queries !== false;
455
+ const generateMutations = tableOps?.mutations !== false && this.config.mutations !== false;
456
+ if (!generateQueries && !generateMutations)
457
+ continue;
458
+ const tableTypes = this.generateTableTypes(tableName);
459
+ const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
460
+ const { selectSingleOutput, selectArrOutput, singleTableItemOutput, arrTableItemOutput } = tableTypes.outputs;
461
+ if (generateQueries) {
462
+ const selectArr = this.createQueryResolver(tableName, tableOrder, tableFilters);
463
+ queries[selectArr.name] = {
464
+ type: selectArrOutput,
465
+ args: selectArr.args,
466
+ resolve: selectArr.resolver
467
+ };
468
+ const selectSingle = this.createSingleQueryResolver(tableName, tableOrder, tableFilters);
469
+ queries[selectSingle.name] = {
470
+ type: selectSingleOutput,
471
+ args: selectSingle.args,
472
+ resolve: selectSingle.resolver
473
+ };
474
+ const selectCount = this.createCountResolver(tableName, tableFilters);
475
+ queries[selectCount.name] = {
476
+ type: GraphQLInt2,
477
+ args: selectCount.args,
478
+ resolve: selectCount.resolver
479
+ };
480
+ inputs[tableFilters.name] = tableFilters;
481
+ inputs[tableOrder.name] = tableOrder;
482
+ outputs[selectSingleOutput.name] = selectSingleOutput;
483
+ }
484
+ if (generateMutations) {
485
+ const insertArr = this.createInsertResolver(tableName, insertInput);
486
+ mutations[insertArr.name] = {
487
+ type: arrTableItemOutput,
488
+ args: insertArr.args,
489
+ resolve: insertArr.resolver
490
+ };
491
+ const insertSingle = this.createInsertSingleResolver(tableName, insertInput);
492
+ mutations[insertSingle.name] = {
493
+ type: singleTableItemOutput,
494
+ args: insertSingle.args,
495
+ resolve: insertSingle.resolver
496
+ };
497
+ const update = this.createUpdateResolver(tableName, updateInput, tableFilters);
498
+ mutations[update.name] = {
499
+ type: arrTableItemOutput,
500
+ args: update.args,
501
+ resolve: update.resolver
502
+ };
503
+ const del = this.createDeleteResolver(tableName, tableFilters);
504
+ mutations[del.name] = {
505
+ type: arrTableItemOutput,
506
+ args: del.args,
507
+ resolve: del.resolver
508
+ };
509
+ inputs[insertInput.name] = insertInput;
510
+ inputs[updateInput.name] = updateInput;
511
+ inputs[tableFilters.name] = tableFilters;
512
+ inputs[tableOrder.name] = tableOrder;
513
+ outputs[singleTableItemOutput.name] = singleTableItemOutput;
514
+ }
515
+ }
516
+ return { queries, mutations, inputs, types: outputs };
517
+ }
518
+ build() {
519
+ const entities = this.buildEntities();
520
+ const { queries, mutations, inputs, types: outputs } = entities;
521
+ const graphQLSchemaConfig = {
522
+ types: [...Object.values(inputs), ...Object.values(outputs)],
523
+ query: new GraphQLObjectType2({
524
+ name: "Query",
525
+ fields: queries
526
+ })
527
+ };
528
+ if (this.config.mutations !== false) {
529
+ graphQLSchemaConfig.mutation = new GraphQLObjectType2({
530
+ name: "Mutation",
531
+ fields: mutations
532
+ });
533
+ }
534
+ const schema = new GraphQLSchema(graphQLSchemaConfig);
535
+ this.logDebugInfo(schema);
536
+ return { schema, entities };
537
+ }
538
+ logDebugInfo(schema) {
539
+ const debug = this.config.debug;
540
+ if (!debug)
541
+ return;
542
+ const showSize = debug === true || typeof debug === "object" && debug.schemaSize !== false;
543
+ const showTree = typeof debug === "object" && debug.relationTree === true;
544
+ if (showSize) {
545
+ const sdl = printSchema(schema);
546
+ const typeCount = Object.keys(schema.getTypeMap()).filter((t) => !t.startsWith("__")).length;
547
+ console.info(`[drizzle-graphql] Schema: ${sdl.length} bytes, ${typeCount} types`);
548
+ }
549
+ if (showTree) {
550
+ for (const [tableName, relations] of Object.entries(this.relationMap)) {
551
+ const relNames = Object.keys(relations);
552
+ if (relNames.length) {
553
+ console.info(`[drizzle-graphql] ${tableName}: ${relNames.join(", ")}`);
554
+ }
555
+ }
556
+ }
557
+ }
558
+ extractTables(schema) {
559
+ const entries = Object.entries(schema).filter(([_, value]) => is3(value, PgTable2));
560
+ if (!entries.length) {
561
+ throw new Error("Drizzle-GraphQL Error: No tables detected in Drizzle-ORM's database instance. Did you forget to pass schema to drizzle constructor?");
562
+ }
563
+ return Object.fromEntries(entries);
564
+ }
565
+ getTable(tableName) {
566
+ const table = this.tables[tableName];
567
+ if (!table) {
568
+ throw new Error(`Drizzle-GraphQL Error: Table '${tableName}' not found.`);
569
+ }
570
+ return table;
571
+ }
572
+ buildRelationMap(schema) {
573
+ const schemaEntries = Object.entries(schema);
574
+ const tableEntries = Object.entries(this.tables);
575
+ const rawRelations = schemaEntries.filter(([_, value]) => is3(value, Relations)).map(([_, value]) => {
576
+ const entry = tableEntries.find(([__, tableValue]) => tableValue === value.table);
577
+ if (!entry)
578
+ return null;
579
+ return [entry[0], value];
580
+ }).filter((entry) => entry !== null).map(([tableName, relValue]) => [
581
+ tableName,
582
+ relValue.config(createTableRelationsHelpers(this.getTable(tableName)))
583
+ ]);
584
+ return Object.fromEntries(rawRelations.map(([relName, config]) => {
585
+ const namedConfig = Object.fromEntries(Object.entries(config).map(([innerRelName, innerRelValue]) => {
586
+ const targetEntry = tableEntries.find(([_, tableValue]) => tableValue === innerRelValue.referencedTable);
587
+ if (!targetEntry)
588
+ return null;
589
+ return [
590
+ innerRelName,
591
+ { relation: innerRelValue, targetTableName: targetEntry[0] }
592
+ ];
593
+ }).filter((entry) => entry !== null));
594
+ return [relName, namedConfig];
595
+ }));
596
+ }
597
+ getFilterType(tableName) {
598
+ const cached = this.filterTypes.get(tableName);
599
+ if (cached)
600
+ return cached;
601
+ const filters = new GraphQLInputObjectType2({
602
+ name: `${capitalize(tableName)}Filters`,
603
+ fields: () => {
604
+ const filterColumns = this.getFilterValues(tableName);
605
+ const relationFilterFields = this.buildRelationFilterFields(tableName);
606
+ return {
607
+ ...filterColumns,
608
+ ...relationFilterFields,
609
+ OR: {
610
+ type: new GraphQLList2(new GraphQLNonNull2(new GraphQLInputObjectType2({
611
+ name: `${capitalize(tableName)}FiltersOr`,
612
+ fields: () => ({
613
+ ...filterColumns,
614
+ ...relationFilterFields
615
+ })
616
+ })))
617
+ }
618
+ };
619
+ }
620
+ });
621
+ this.filterTypes.set(tableName, filters);
622
+ return filters;
623
+ }
624
+ getOrderByType(tableName) {
625
+ const cached = this.orderByTypes.get(tableName);
626
+ if (cached)
627
+ return cached;
628
+ const table = this.getTable(tableName);
629
+ const columns = getTableColumns2(table);
630
+ const orderColumns = Object.fromEntries(Object.entries(columns).map(([columnName]) => [columnName, { type: this.innerOrder }]));
631
+ const order = new GraphQLInputObjectType2({
632
+ name: `${capitalize(tableName)}OrderBy`,
633
+ fields: orderColumns
634
+ });
635
+ this.orderByTypes.set(tableName, order);
636
+ return order;
637
+ }
638
+ getFilterValues(tableName) {
639
+ const cached = this.filterValueTypes.get(tableName);
640
+ if (cached)
641
+ return cached;
642
+ const table = this.getTable(tableName);
643
+ const columns = getTableColumns2(table);
644
+ const result = Object.fromEntries(Object.entries(columns).map(([columnName, col]) => [
645
+ columnName,
646
+ { type: this.generateColumnFilterValues(col, tableName, columnName) }
647
+ ]));
648
+ this.filterValueTypes.set(tableName, result);
649
+ return result;
650
+ }
651
+ getSelectFields(tableName) {
652
+ const cached = this.selectFieldTypes.get(tableName);
653
+ if (cached)
654
+ return cached;
655
+ const table = this.getTable(tableName);
656
+ const columns = getTableColumns2(table);
657
+ const result = Object.fromEntries(Object.entries(columns).map(([columnName, col]) => [
658
+ columnName,
659
+ drizzleColumnToGraphQLType(col, columnName, tableName)
660
+ ]));
661
+ this.selectFieldTypes.set(tableName, result);
662
+ return result;
663
+ }
664
+ generateColumnFilterValues(column, tableName, columnName) {
665
+ const columnGraphQLType = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);
666
+ const columnArr = new GraphQLList2(new GraphQLNonNull2(columnGraphQLType.type));
667
+ const baseFields = {
668
+ eq: { type: columnGraphQLType.type, description: columnGraphQLType.description },
669
+ ne: { type: columnGraphQLType.type, description: columnGraphQLType.description },
670
+ lt: { type: columnGraphQLType.type, description: columnGraphQLType.description },
671
+ lte: { type: columnGraphQLType.type, description: columnGraphQLType.description },
672
+ gt: { type: columnGraphQLType.type, description: columnGraphQLType.description },
673
+ gte: { type: columnGraphQLType.type, description: columnGraphQLType.description },
674
+ like: { type: GraphQLString2 },
675
+ notLike: { type: GraphQLString2 },
676
+ ilike: { type: GraphQLString2 },
677
+ notIlike: { type: GraphQLString2 },
678
+ inArray: { type: columnArr, description: `Array<${columnGraphQLType.description}>` },
679
+ notInArray: { type: columnArr, description: `Array<${columnGraphQLType.description}>` },
680
+ isNull: { type: GraphQLBoolean2 },
681
+ isNotNull: { type: GraphQLBoolean2 }
682
+ };
683
+ return new GraphQLInputObjectType2({
684
+ name: `${capitalize(tableName)}${capitalize(columnName)}Filters`,
685
+ fields: {
686
+ ...baseFields,
687
+ OR: {
688
+ type: new GraphQLList2(new GraphQLNonNull2(new GraphQLInputObjectType2({
689
+ name: `${capitalize(tableName)}${capitalize(columnName)}FiltersOr`,
690
+ fields: baseFields
691
+ })))
692
+ }
693
+ }
694
+ });
695
+ }
696
+ generateSelectFields(tableName, typeName, withOrder, currentDepth = 0, usedTables = new Set, currentSelfDepth = 0, forceLeaf, allowedRelations) {
697
+ const relations = this.relationMap[tableName];
698
+ const relationEntries = relations ? Object.entries(relations) : [];
699
+ const order = withOrder ? this.getOrderByType(tableName) : undefined;
700
+ const filters = this.getFilterType(tableName);
701
+ const tableFields = this.getSelectFields(tableName);
702
+ if (forceLeaf || typeof this.limitRelationDepth !== "number" && usedTables.has(tableName) || typeof this.limitRelationDepth === "number" && currentDepth >= this.limitRelationDepth || !relationEntries.length) {
703
+ return { order, filters, tableFields, relationFields: {} };
704
+ }
705
+ const rawRelationFields = [];
706
+ const updatedUsedTables = new Set(usedTables).add(tableName);
707
+ const newDepth = currentDepth + 1;
708
+ for (const [relationName, { targetTableName, relation }] of relationEntries) {
709
+ if (allowedRelations && !allowedRelations.includes(relationName))
710
+ continue;
711
+ const pruneRule = this.pruneRelations.get(`${tableName}.${relationName}`);
712
+ if (pruneRule === false)
713
+ continue;
714
+ const relTypeName = `${typeName}${capitalize(relationName)}Relation`;
715
+ const isOne = is3(relation, One);
716
+ const isSelfRelation = targetTableName === tableName;
717
+ if (isSelfRelation && currentSelfDepth + 1 >= this.limitSelfRelationDepth) {
718
+ continue;
719
+ }
720
+ const isCrossCycle = !isSelfRelation && usedTables.has(targetTableName);
721
+ const effectiveDepth = isCrossCycle && typeof this.limitRelationDepth === "number" ? Math.max(newDepth, this.limitRelationDepth - 1) : newDepth;
722
+ const isLeaf = pruneRule === "leaf";
723
+ const onlyRels = pruneRule && typeof pruneRule === "object" ? pruneRule.only : undefined;
724
+ const relData = this.generateSelectFields(targetTableName, relTypeName, !isOne, effectiveDepth, updatedUsedTables, isSelfRelation ? currentSelfDepth + 1 : 0, isLeaf, onlyRels);
725
+ const relType = new GraphQLObjectType2({
726
+ name: relTypeName,
727
+ fields: this.toFieldMap(relData.tableFields, relData.relationFields)
728
+ });
729
+ if (isOne) {
730
+ rawRelationFields.push([
731
+ relationName,
732
+ {
733
+ type: relType,
734
+ args: {
735
+ where: { type: relData.filters }
736
+ }
737
+ }
738
+ ]);
739
+ continue;
740
+ }
741
+ rawRelationFields.push([
742
+ relationName,
743
+ {
744
+ type: new GraphQLNonNull2(new GraphQLList2(new GraphQLNonNull2(relType))),
745
+ args: {
746
+ where: { type: relData.filters },
747
+ orderBy: { type: relData.order },
748
+ offset: { type: GraphQLInt2 },
749
+ limit: { type: GraphQLInt2 }
750
+ }
751
+ }
752
+ ]);
753
+ }
754
+ const relationFields = Object.fromEntries(rawRelationFields);
755
+ return { order, filters, tableFields, relationFields };
756
+ }
757
+ generateTableTypes(tableName) {
758
+ const stylizedName = capitalize(tableName);
759
+ const { tableFields, relationFields, filters, order } = this.generateSelectFields(tableName, stylizedName, true);
760
+ const table = this.getTable(tableName);
761
+ const columns = getTableColumns2(table);
762
+ const columnEntries = Object.entries(columns);
763
+ const insertFields = Object.fromEntries(columnEntries.map(([columnName, col]) => [
764
+ columnName,
765
+ drizzleColumnToGraphQLType(col, columnName, tableName, false, true, true)
766
+ ]));
767
+ const updateFields = Object.fromEntries(columnEntries.map(([columnName, col]) => [
768
+ columnName,
769
+ drizzleColumnToGraphQLType(col, columnName, tableName, true, false, true)
770
+ ]));
771
+ const insertInput = new GraphQLInputObjectType2({
772
+ name: `${stylizedName}InsertInput`,
773
+ fields: insertFields
774
+ });
775
+ const updateInput = new GraphQLInputObjectType2({
776
+ name: `${stylizedName}UpdateInput`,
777
+ fields: updateFields
778
+ });
779
+ const selectSingleOutput = new GraphQLObjectType2({
780
+ name: `${stylizedName}SelectItem`,
781
+ fields: this.toFieldMap(tableFields, relationFields)
782
+ });
783
+ const selectArrOutput = new GraphQLNonNull2(new GraphQLList2(new GraphQLNonNull2(selectSingleOutput)));
784
+ const singleTableItemOutput = new GraphQLObjectType2({
785
+ name: `${stylizedName}Item`,
786
+ fields: this.toFieldMap(tableFields)
787
+ });
788
+ const arrTableItemOutput = new GraphQLNonNull2(new GraphQLList2(new GraphQLNonNull2(singleTableItemOutput)));
789
+ return {
790
+ inputs: {
791
+ insertInput,
792
+ updateInput,
793
+ tableOrder: order,
794
+ tableFilters: filters
795
+ },
796
+ outputs: {
797
+ selectSingleOutput,
798
+ selectArrOutput,
799
+ singleTableItemOutput,
800
+ arrTableItemOutput
801
+ }
802
+ };
803
+ }
804
+ buildRelationFilterFields(tableName) {
805
+ const relations = this.relationMap[tableName];
806
+ if (!relations)
807
+ return {};
808
+ const fields = {};
809
+ for (const [relationName, { targetTableName, relation }] of Object.entries(relations)) {
810
+ if (is3(relation, One)) {
811
+ fields[relationName] = {
812
+ type: this.getFilterType(targetTableName)
813
+ };
814
+ } else {
815
+ fields[relationName] = {
816
+ type: new GraphQLInputObjectType2({
817
+ name: `${capitalize(tableName)}${capitalize(relationName)}RelFilter`,
818
+ fields: () => ({
819
+ some: { type: this.getFilterType(targetTableName) },
820
+ every: { type: this.getFilterType(targetTableName) },
821
+ none: { type: this.getFilterType(targetTableName) }
822
+ })
823
+ })
824
+ };
825
+ }
826
+ }
827
+ return fields;
828
+ }
829
+ createQueryResolver(tableName, orderArgs, filterArgs) {
830
+ const queryName = `${uncapitalize(tableName)}${this.suffixes.list}`;
831
+ const typeName = `${capitalize(tableName)}SelectItem`;
832
+ const table = this.getTable(tableName);
833
+ const queryBase = this.db.query[tableName];
834
+ if (!queryBase) {
835
+ throw new Error(`Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`);
836
+ }
837
+ const args = {
838
+ offset: { type: GraphQLInt2 },
839
+ limit: { type: GraphQLInt2 },
840
+ orderBy: { type: orderArgs },
841
+ where: { type: filterArgs }
842
+ };
843
+ const resolver = async (_source, resolverArgs, context, info) => {
844
+ return this.executeWithHooks(tableName, "query", resolverArgs, context, info, async (finalArgs) => {
845
+ try {
846
+ const { offset, limit, orderBy, where } = finalArgs;
847
+ const parsedInfo = parseResolveInfo(info, { deep: true });
848
+ const query = queryBase.findMany({
849
+ columns: this.extractColumns(this.getFieldsByTypeName(parsedInfo, typeName), table),
850
+ offset,
851
+ limit,
852
+ orderBy: orderBy ? this.extractOrderBy(table, orderBy) : undefined,
853
+ where: where ? this.extractAllFilters(table, tableName, where) : undefined,
854
+ with: this.relationMap[tableName] ? this.extractRelationsParams(tableName, parsedInfo, typeName) : undefined
855
+ });
856
+ const result = await query;
857
+ return remapToGraphQLArrayOutput(result, tableName, table, this.relationMap);
858
+ } catch (e) {
859
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
860
+ throw new GraphQLError2(e.message);
861
+ }
862
+ throw e;
863
+ }
864
+ });
865
+ };
866
+ return { name: queryName, resolver, args };
867
+ }
868
+ createSingleQueryResolver(tableName, orderArgs, filterArgs) {
869
+ const queryName = `${uncapitalize(tableName)}${this.suffixes.single}`;
870
+ const typeName = `${capitalize(tableName)}SelectItem`;
871
+ const table = this.getTable(tableName);
872
+ const queryBase = this.db.query[tableName];
873
+ if (!queryBase) {
874
+ throw new Error(`Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`);
875
+ }
876
+ const args = {
877
+ offset: { type: GraphQLInt2 },
878
+ orderBy: { type: orderArgs },
879
+ where: { type: filterArgs }
880
+ };
881
+ const resolver = async (_source, resolverArgs, context, info) => {
882
+ return this.executeWithHooks(tableName, "querySingle", resolverArgs, context, info, async (finalArgs) => {
883
+ try {
884
+ const { offset, orderBy, where } = finalArgs;
885
+ const parsedInfo = parseResolveInfo(info, { deep: true });
886
+ const query = queryBase.findFirst({
887
+ columns: this.extractColumns(this.getFieldsByTypeName(parsedInfo, typeName), table),
888
+ offset,
889
+ orderBy: orderBy ? this.extractOrderBy(table, orderBy) : undefined,
890
+ where: where ? this.extractAllFilters(table, tableName, where) : undefined,
891
+ with: this.relationMap[tableName] ? this.extractRelationsParams(tableName, parsedInfo, typeName) : undefined
892
+ });
893
+ const result = await query;
894
+ if (!result)
895
+ return;
896
+ return remapToGraphQLSingleOutput(result, tableName, table, this.relationMap);
897
+ } catch (e) {
898
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
899
+ throw new GraphQLError2(e.message);
900
+ }
901
+ throw e;
902
+ }
903
+ });
904
+ };
905
+ return { name: queryName, resolver, args };
906
+ }
907
+ createCountResolver(tableName, filterArgs) {
908
+ const queryName = `${uncapitalize(tableName)}Count`;
909
+ const table = this.getTable(tableName);
910
+ const args = {
911
+ where: { type: filterArgs }
912
+ };
913
+ const resolver = async (_source, resolverArgs, context, info) => {
914
+ return this.executeWithHooks(tableName, "count", resolverArgs, context, info, async (finalArgs) => {
915
+ try {
916
+ const { where } = finalArgs;
917
+ const whereClause = where ? this.extractAllFilters(table, tableName, where) : undefined;
918
+ return await this.executeCountQuery(table, whereClause);
919
+ } catch (e) {
920
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
921
+ throw new GraphQLError2(e.message);
922
+ }
923
+ throw e;
924
+ }
925
+ });
926
+ };
927
+ return { name: queryName, resolver, args };
928
+ }
929
+ createInsertResolver(tableName, baseType) {
930
+ const queryName = `insertInto${capitalize(tableName)}`;
931
+ const typeName = `${capitalize(tableName)}Item`;
932
+ const table = this.getTable(tableName);
933
+ const args = {
934
+ values: { type: new GraphQLNonNull2(new GraphQLList2(new GraphQLNonNull2(baseType))) }
935
+ };
936
+ const resolver = async (_source, resolverArgs, context, info) => {
937
+ return this.executeWithHooks(tableName, "insert", resolverArgs, context, info, async (finalArgs) => {
938
+ try {
939
+ const input = remapFromGraphQLArrayInput(finalArgs.values, table);
940
+ if (!input.length)
941
+ throw new GraphQLError2("No values were provided!");
942
+ const parsedInfo = parseResolveInfo(info, { deep: true });
943
+ const columns = this.extractColumnsSQLFormat(this.getFieldsByTypeName(parsedInfo, typeName), table);
944
+ const result = await this.adapter.executeInsert(this.db, table, input, columns);
945
+ return remapToGraphQLArrayOutput(result, tableName, table);
946
+ } catch (e) {
947
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
948
+ throw new GraphQLError2(e.message);
949
+ }
950
+ throw e;
951
+ }
952
+ });
953
+ };
954
+ return { name: queryName, resolver, args };
955
+ }
956
+ createInsertSingleResolver(tableName, baseType) {
957
+ const queryName = `insertInto${capitalize(tableName)}Single`;
958
+ const typeName = `${capitalize(tableName)}Item`;
959
+ const table = this.getTable(tableName);
960
+ const args = {
961
+ values: { type: new GraphQLNonNull2(baseType) }
962
+ };
963
+ const resolver = async (_source, resolverArgs, context, info) => {
964
+ return this.executeWithHooks(tableName, "insertSingle", resolverArgs, context, info, async (finalArgs) => {
965
+ try {
966
+ const input = remapFromGraphQLSingleInput(finalArgs.values, table);
967
+ const parsedInfo = parseResolveInfo(info, { deep: true });
968
+ const columns = this.extractColumnsSQLFormat(this.getFieldsByTypeName(parsedInfo, typeName), table);
969
+ const result = await this.adapter.executeInsert(this.db, table, [input], columns);
970
+ if (!result[0])
971
+ return;
972
+ return remapToGraphQLSingleOutput(result[0], tableName, table);
973
+ } catch (e) {
974
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
975
+ throw new GraphQLError2(e.message);
976
+ }
977
+ throw e;
978
+ }
979
+ });
980
+ };
981
+ return { name: queryName, resolver, args };
982
+ }
983
+ createUpdateResolver(tableName, setArgs, filterArgs) {
984
+ const queryName = `update${capitalize(tableName)}`;
985
+ const typeName = `${capitalize(tableName)}Item`;
986
+ const table = this.getTable(tableName);
987
+ const args = {
988
+ set: { type: new GraphQLNonNull2(setArgs) },
989
+ where: { type: filterArgs }
990
+ };
991
+ const resolver = async (_source, resolverArgs, context, info) => {
992
+ return this.executeWithHooks(tableName, "update", resolverArgs, context, info, async (finalArgs) => {
993
+ try {
994
+ const { where, set } = finalArgs;
995
+ const parsedInfo = parseResolveInfo(info, { deep: true });
996
+ const columns = this.extractColumnsSQLFormat(this.getFieldsByTypeName(parsedInfo, typeName), table);
997
+ const input = remapFromGraphQLSingleInput(set, table);
998
+ if (!Object.keys(input).length)
999
+ throw new GraphQLError2("Unable to update with no values specified!");
1000
+ const whereClause = where ? this.extractAllFilters(table, tableName, where) : undefined;
1001
+ const result = await this.adapter.executeUpdate(this.db, table, input, whereClause, columns);
1002
+ return remapToGraphQLArrayOutput(result, tableName, table);
1003
+ } catch (e) {
1004
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
1005
+ throw new GraphQLError2(e.message);
1006
+ }
1007
+ throw e;
1008
+ }
1009
+ });
1010
+ };
1011
+ return { name: queryName, resolver, args };
1012
+ }
1013
+ createDeleteResolver(tableName, filterArgs) {
1014
+ const queryName = `deleteFrom${capitalize(tableName)}`;
1015
+ const typeName = `${capitalize(tableName)}Item`;
1016
+ const table = this.getTable(tableName);
1017
+ const args = {
1018
+ where: { type: filterArgs }
1019
+ };
1020
+ const resolver = async (_source, resolverArgs, context, info) => {
1021
+ return this.executeWithHooks(tableName, "delete", resolverArgs, context, info, async (finalArgs) => {
1022
+ try {
1023
+ const { where } = finalArgs;
1024
+ const parsedInfo = parseResolveInfo(info, { deep: true });
1025
+ const columns = this.extractColumnsSQLFormat(this.getFieldsByTypeName(parsedInfo, typeName), table);
1026
+ const whereClause = where ? this.extractAllFilters(table, tableName, where) : undefined;
1027
+ const result = await this.adapter.executeDelete(this.db, table, whereClause, columns);
1028
+ return remapToGraphQLArrayOutput(result, tableName, table);
1029
+ } catch (e) {
1030
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") {
1031
+ throw new GraphQLError2(e.message);
1032
+ }
1033
+ throw e;
1034
+ }
1035
+ });
1036
+ };
1037
+ return { name: queryName, resolver, args };
1038
+ }
1039
+ extractColumnFilters(column, columnName, operators) {
1040
+ if (!operators.OR?.length)
1041
+ delete operators.OR;
1042
+ const entries = Object.entries(operators);
1043
+ if (operators.OR) {
1044
+ if (entries.length > 1) {
1045
+ throw new GraphQLError2(`WHERE ${columnName}: Cannot specify both fields and 'OR' in column operators!`);
1046
+ }
1047
+ const variants2 = [];
1048
+ for (const variant of operators.OR) {
1049
+ const extracted = this.extractColumnFilters(column, columnName, variant);
1050
+ if (extracted)
1051
+ variants2.push(extracted);
1052
+ }
1053
+ return variants2.length ? variants2.length > 1 ? or(...variants2) : variants2[0] : undefined;
1054
+ }
1055
+ const comparisonOps = { eq, ne, gt, gte, lt, lte };
1056
+ const stringOps = { like, notLike, ilike, notIlike };
1057
+ const arrayOps = {
1058
+ inArray,
1059
+ notInArray
1060
+ };
1061
+ const nullOps = { isNull, isNotNull };
1062
+ const variants = [];
1063
+ for (const [operatorName, operatorValue] of entries) {
1064
+ if (operatorValue === null || operatorValue === false)
1065
+ continue;
1066
+ if (operatorName in comparisonOps) {
1067
+ const op = comparisonOps[operatorName];
1068
+ if (op) {
1069
+ const singleValue = remapFromGraphQLCore(operatorValue, column, columnName);
1070
+ variants.push(op(column, singleValue));
1071
+ }
1072
+ } else if (operatorName in stringOps) {
1073
+ const op = stringOps[operatorName];
1074
+ if (op)
1075
+ variants.push(op(column, operatorValue));
1076
+ } else if (operatorName in arrayOps) {
1077
+ const op = arrayOps[operatorName];
1078
+ if (op) {
1079
+ if (!operatorValue.length) {
1080
+ throw new GraphQLError2(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
1081
+ }
1082
+ const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
1083
+ variants.push(op(column, arrayValue));
1084
+ }
1085
+ } else if (operatorName in nullOps) {
1086
+ const op = nullOps[operatorName];
1087
+ if (op)
1088
+ variants.push(op(column));
1089
+ }
1090
+ }
1091
+ return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : undefined;
1092
+ }
1093
+ extractTableColumnFilters(table, tableName, filters) {
1094
+ if (!filters.OR?.length)
1095
+ delete filters.OR;
1096
+ const tableColumns = getTableColumns2(table);
1097
+ const columnEntries = [];
1098
+ const relationEntries = [];
1099
+ for (const [key, value] of Object.entries(filters)) {
1100
+ if (key === "OR")
1101
+ continue;
1102
+ if (value === null)
1103
+ continue;
1104
+ if (tableColumns[key]) {
1105
+ columnEntries.push([key, value]);
1106
+ } else {
1107
+ relationEntries.push([key, value]);
1108
+ }
1109
+ }
1110
+ if (filters.OR) {
1111
+ if (columnEntries.length > 0 || relationEntries.length > 0) {
1112
+ throw new GraphQLError2(`WHERE ${tableName}: Cannot specify both fields and 'OR' in table filters!`);
1113
+ }
1114
+ const variants2 = [];
1115
+ for (const variant of filters.OR) {
1116
+ const extracted = this.extractAllFilters(table, tableName, variant);
1117
+ if (extracted)
1118
+ variants2.push(extracted);
1119
+ }
1120
+ return variants2.length ? variants2.length > 1 ? or(...variants2) : variants2[0] : undefined;
1121
+ }
1122
+ const variants = [];
1123
+ for (const [columnName, operators] of columnEntries) {
1124
+ const column = tableColumns[columnName];
1125
+ if (!column)
1126
+ continue;
1127
+ const result = this.extractColumnFilters(column, columnName, operators);
1128
+ if (result)
1129
+ variants.push(result);
1130
+ }
1131
+ for (const [relationName, filterValue] of relationEntries) {
1132
+ const result = this.extractRelationFilters(table, tableName, relationName, filterValue);
1133
+ if (result)
1134
+ variants.push(result);
1135
+ }
1136
+ return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : undefined;
1137
+ }
1138
+ extractAllFilters(table, tableName, filters) {
1139
+ return this.extractTableColumnFilters(table, tableName, filters);
1140
+ }
1141
+ extractRelationFilters(table, tableName, relationName, filterValue) {
1142
+ const rel = this.relationMap[tableName]?.[relationName];
1143
+ if (!rel)
1144
+ return;
1145
+ const { targetTableName, relation } = rel;
1146
+ const targetTable = this.getTable(targetTableName);
1147
+ const isOne = is3(relation, One);
1148
+ if (isOne) {
1149
+ return this.buildExistsSubquery(table, targetTable, relation, targetTableName, filterValue, "some");
1150
+ } else {
1151
+ const variants = [];
1152
+ if (filterValue.some) {
1153
+ const result = this.buildExistsSubquery(table, targetTable, relation, targetTableName, filterValue.some, "some");
1154
+ if (result)
1155
+ variants.push(result);
1156
+ }
1157
+ if (filterValue.every) {
1158
+ const result = this.buildExistsSubquery(table, targetTable, relation, targetTableName, filterValue.every, "every");
1159
+ if (result)
1160
+ variants.push(result);
1161
+ }
1162
+ if (filterValue.none) {
1163
+ const result = this.buildExistsSubquery(table, targetTable, relation, targetTableName, filterValue.none, "none");
1164
+ if (result)
1165
+ variants.push(result);
1166
+ }
1167
+ return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : undefined;
1168
+ }
1169
+ }
1170
+ buildExistsSubquery(parentTable, targetTable, relation, targetTableName, filterValue, quantifier) {
1171
+ const joinCondition = this.buildJoinCondition(parentTable, targetTable, relation);
1172
+ if (!joinCondition)
1173
+ return;
1174
+ const innerFilter = this.extractAllFilters(targetTable, targetTableName, filterValue);
1175
+ const whereClause = innerFilter ? and(joinCondition, innerFilter) : joinCondition;
1176
+ if (!whereClause)
1177
+ return;
1178
+ const subquery = this.db.select({ _: sql`1` }).from(targetTable).where(whereClause);
1179
+ switch (quantifier) {
1180
+ case "some":
1181
+ return exists(subquery);
1182
+ case "none":
1183
+ return not(exists(subquery));
1184
+ case "every": {
1185
+ if (!innerFilter)
1186
+ return;
1187
+ const negatedFilter = not(innerFilter);
1188
+ const everyWhereClause = and(joinCondition, negatedFilter);
1189
+ if (!everyWhereClause)
1190
+ return;
1191
+ const everySubquery = this.db.select({ _: sql`1` }).from(targetTable).where(everyWhereClause);
1192
+ return not(exists(everySubquery));
1193
+ }
1194
+ }
1195
+ }
1196
+ buildJoinCondition(parentTable, _targetTable, relation) {
1197
+ try {
1198
+ const { fields, references } = normalizeRelation(this.relationalSchema, this.tableNamesMap, relation);
1199
+ if (!fields?.length || !references?.length)
1200
+ return;
1201
+ const parentName = getTableName(parentTable);
1202
+ const conditions = [];
1203
+ for (let i = 0;i < fields.length; i++) {
1204
+ const field = fields[i];
1205
+ const ref = references[i];
1206
+ if (!field || !ref)
1207
+ continue;
1208
+ if (getTableName(field.table) === parentName) {
1209
+ conditions.push(sql`${sql.identifier(parentName)}.${sql.identifier(field.name)} = ${ref}`);
1210
+ } else if (getTableName(ref.table) === parentName) {
1211
+ conditions.push(sql`${field} = ${sql.identifier(parentName)}.${sql.identifier(ref.name)}`);
1212
+ } else {
1213
+ conditions.push(eq(field, ref));
1214
+ }
1215
+ }
1216
+ return conditions.length > 1 ? and(...conditions) : conditions[0];
1217
+ } catch {
1218
+ return;
1219
+ }
1220
+ }
1221
+ extractOrderBy(table, orderArgs) {
1222
+ const res = [];
1223
+ for (const [column, config] of Object.entries(orderArgs).sort((a, b) => {
1224
+ const ap = a[1]?.priority ?? 0;
1225
+ const bp = b[1]?.priority ?? 0;
1226
+ return bp - ap;
1227
+ })) {
1228
+ if (!config)
1229
+ continue;
1230
+ const { direction } = config;
1231
+ const col = getTableColumns2(table)[column];
1232
+ if (!col)
1233
+ continue;
1234
+ res.push(direction === "asc" ? asc(col) : desc(col));
1235
+ }
1236
+ return res;
1237
+ }
1238
+ extractColumns(tree, table) {
1239
+ const tableColumns = getTableColumns2(table);
1240
+ const selectedColumns = [];
1241
+ for (const [_, fieldData] of Object.entries(tree)) {
1242
+ if (!tableColumns[fieldData.name])
1243
+ continue;
1244
+ selectedColumns.push([fieldData.name, true]);
1245
+ }
1246
+ if (!selectedColumns.length) {
1247
+ const columnKeys = Object.entries(tableColumns);
1248
+ const columnName = columnKeys.find((e) => !rqbCrashTypes.includes(e[1].columnType))?.[0] ?? columnKeys[0]?.[0] ?? "";
1249
+ selectedColumns.push([columnName, true]);
1250
+ }
1251
+ return Object.fromEntries(selectedColumns);
1252
+ }
1253
+ extractColumnsSQLFormat(tree, table) {
1254
+ const tableColumns = getTableColumns2(table);
1255
+ const selectedColumns = [];
1256
+ for (const [_, fieldData] of Object.entries(tree)) {
1257
+ const col = tableColumns[fieldData.name];
1258
+ if (!col)
1259
+ continue;
1260
+ selectedColumns.push([fieldData.name, col]);
1261
+ }
1262
+ if (!selectedColumns.length) {
1263
+ const columnKeys = Object.entries(tableColumns);
1264
+ const columnName = columnKeys.find((e) => !rqbCrashTypes.includes(e[1].columnType))?.[0] ?? columnKeys[0]?.[0] ?? "";
1265
+ const fallbackCol = tableColumns[columnName];
1266
+ if (fallbackCol)
1267
+ selectedColumns.push([columnName, fallbackCol]);
1268
+ }
1269
+ return Object.fromEntries(selectedColumns);
1270
+ }
1271
+ getFieldsByTypeName(info, typeName) {
1272
+ return info.fieldsByTypeName[typeName] ?? {};
1273
+ }
1274
+ extractRelationsParams(tableName, info, typeName) {
1275
+ return this.extractRelationsParamsInner(tableName, typeName, info, true);
1276
+ }
1277
+ extractRelationsParamsInner(tableName, typeName, originField, isInitial = false) {
1278
+ const relations = this.relationMap[tableName];
1279
+ if (!relations)
1280
+ return;
1281
+ const baseField = Object.entries(originField.fieldsByTypeName).find(([key]) => key === typeName)?.[1];
1282
+ if (!baseField)
1283
+ return;
1284
+ const args = {};
1285
+ for (const [relName, { targetTableName }] of Object.entries(relations)) {
1286
+ const relTypeName = `${isInitial ? capitalize(tableName) : typeName}${capitalize(relName)}Relation`;
1287
+ const relFieldSelection = Object.values(baseField).find((field) => field.name === relName)?.fieldsByTypeName[relTypeName];
1288
+ if (!relFieldSelection)
1289
+ continue;
1290
+ const targetTable = this.getTable(targetTableName);
1291
+ const columns = this.extractColumns(relFieldSelection, targetTable);
1292
+ const thisRecord = { columns };
1293
+ const relationField = Object.values(baseField).find((e) => e.name === relName);
1294
+ const relationArgs = relationField?.args;
1295
+ thisRecord.orderBy = relationArgs?.orderBy ? this.extractOrderBy(targetTable, relationArgs.orderBy) : undefined;
1296
+ thisRecord.where = relationArgs?.where ? this.extractAllFilters(targetTable, relName, relationArgs.where) : undefined;
1297
+ thisRecord.offset = relationArgs?.offset ?? undefined;
1298
+ thisRecord.limit = relationArgs?.limit ?? undefined;
1299
+ thisRecord.with = relationField ? this.extractRelationsParamsInner(targetTableName, relTypeName, relationField) : undefined;
1300
+ args[relName] = thisRecord;
1301
+ }
1302
+ return args;
1303
+ }
1304
+ async executeWithHooks(tableName, operation, args, context, info, execute) {
1305
+ const tableHooks = this.hooks[tableName];
1306
+ if (!tableHooks)
1307
+ return execute(args);
1308
+ const opHooks = tableHooks[operation];
1309
+ if (!opHooks)
1310
+ return execute(args);
1311
+ if ("resolve" in opHooks && opHooks.resolve) {
1312
+ const resolveFn = opHooks.resolve;
1313
+ const defaultResolve = (overrideArgs) => execute(overrideArgs ?? args);
1314
+ return resolveFn({ args, context, info, defaultResolve });
1315
+ }
1316
+ let finalArgs = args;
1317
+ let beforeData;
1318
+ if ("before" in opHooks && opHooks.before) {
1319
+ const beforeResult = await opHooks.before({
1320
+ args,
1321
+ context,
1322
+ info
1323
+ });
1324
+ if (beforeResult) {
1325
+ if (beforeResult.args)
1326
+ finalArgs = beforeResult.args;
1327
+ if (beforeResult.data)
1328
+ beforeData = beforeResult.data;
1329
+ }
1330
+ }
1331
+ const result = await execute(finalArgs);
1332
+ if ("after" in opHooks && opHooks.after) {
1333
+ return opHooks.after({ result, beforeData, context, info });
1334
+ }
1335
+ return result;
1336
+ }
1337
+ async executeCountQuery(table, where) {
1338
+ const db = this.db;
1339
+ if (db.$count && typeof db.$count === "function") {
1340
+ try {
1341
+ const result2 = where ? await db.$count(table, where) : await db.$count(table);
1342
+ return Number(result2) || 0;
1343
+ } catch (_) {}
1344
+ }
1345
+ const query = this.db.select({ count: count() }).from(table);
1346
+ const result = await (where ? query.where(where) : query);
1347
+ const value = result[0]?.count || 0;
1348
+ return Number(value);
1349
+ }
1350
+ }
1351
+ var rqbCrashTypes = ["SQLiteBigInt", "SQLiteBlobJson", "SQLiteBlobBuffer"];
1352
+ // src/codegen.ts
1353
+ import {
1354
+ isEnumType,
1355
+ isInputObjectType,
1356
+ isListType,
1357
+ isNonNullType,
1358
+ isObjectType,
1359
+ isScalarType,
1360
+ printSchema as printSchema2
1361
+ } from "graphql";
1362
+ function getEntitiesFromSchema(schema) {
1363
+ const queryType = schema.getQueryType();
1364
+ const mutationType = schema.getMutationType();
1365
+ const entities = new Map;
1366
+ if (!queryType)
1367
+ return entities;
1368
+ const queryFields = queryType.getFields();
1369
+ const mutationFields = mutationType?.getFields() ?? {};
1370
+ for (const [fieldName] of Object.entries(queryFields)) {
1371
+ if (!fieldName.endsWith("Count"))
1372
+ continue;
1373
+ const tableName = fieldName.slice(0, -5);
1374
+ const listName = queryFields[`${tableName}s`] ? `${tableName}s` : queryFields[tableName] ? tableName : undefined;
1375
+ const singleName = queryFields[tableName] ? tableName : undefined;
1376
+ if (!listName)
1377
+ continue;
1378
+ const listField = queryFields[listName];
1379
+ if (!listField)
1380
+ continue;
1381
+ const selectItemType = unwrapToObjectType(listField.type);
1382
+ if (!selectItemType)
1383
+ continue;
1384
+ const selectFields = selectItemType.getFields();
1385
+ const scalarFields = [];
1386
+ const relations = {};
1387
+ for (const [sfName, sfField] of Object.entries(selectFields)) {
1388
+ const unwrapped = unwrapType(sfField.type);
1389
+ if (isObjectType(unwrapped.type)) {
1390
+ const isList = unwrapped.isList;
1391
+ relations[sfName] = {
1392
+ entity: sfName,
1393
+ type: isList ? "many" : "one",
1394
+ _graphqlType: unwrapped.type
1395
+ };
1396
+ } else {
1397
+ scalarFields.push(sfName);
1398
+ }
1399
+ }
1400
+ const capitalName = capitalize(tableName);
1401
+ const insertName = mutationFields[`insertInto${capitalName}`] ? `insertInto${capitalName}` : undefined;
1402
+ const insertSingleName = mutationFields[`insertInto${capitalName}Single`] ? `insertInto${capitalName}Single` : undefined;
1403
+ const updateName = mutationFields[`update${capitalName}`] ? `update${capitalName}` : undefined;
1404
+ const deleteName = mutationFields[`deleteFrom${capitalName}`] ? `deleteFrom${capitalName}` : undefined;
1405
+ const typeMap = schema.getTypeMap();
1406
+ const hasFilters = `${capitalName}Filters` in typeMap;
1407
+ const hasInsertInput = `${capitalName}InsertInput` in typeMap;
1408
+ const hasUpdateInput = `${capitalName}UpdateInput` in typeMap;
1409
+ const hasOrderBy = `${capitalName}OrderBy` in typeMap;
1410
+ entities.set(tableName, {
1411
+ tableName,
1412
+ typeName: capitalName,
1413
+ fields: scalarFields,
1414
+ relations,
1415
+ queryName: singleName,
1416
+ queryListName: listName,
1417
+ countName: fieldName,
1418
+ insertName,
1419
+ insertSingleName,
1420
+ updateName,
1421
+ deleteName,
1422
+ hasFilters,
1423
+ hasInsertInput,
1424
+ hasUpdateInput,
1425
+ hasOrderBy
1426
+ });
1427
+ }
1428
+ const entityFieldSets = new Map;
1429
+ for (const [name, info] of entities) {
1430
+ entityFieldSets.set(name, new Set(info.fields));
1431
+ }
1432
+ for (const entity of entities.values()) {
1433
+ for (const [, rel] of Object.entries(entity.relations)) {
1434
+ const graphqlType = rel._graphqlType;
1435
+ delete rel._graphqlType;
1436
+ if (!graphqlType)
1437
+ continue;
1438
+ const relFields = graphqlType.getFields();
1439
+ const relScalarNames = new Set;
1440
+ for (const [fname, ffield] of Object.entries(relFields)) {
1441
+ const unwrapped = unwrapType(ffield.type);
1442
+ if (!isObjectType(unwrapped.type)) {
1443
+ relScalarNames.add(fname);
1444
+ }
1445
+ }
1446
+ let bestMatch;
1447
+ let bestOverlap = 0;
1448
+ for (const [entityName, entityFields] of entityFieldSets) {
1449
+ let overlap = 0;
1450
+ for (const f of relScalarNames) {
1451
+ if (entityFields.has(f))
1452
+ overlap++;
1453
+ }
1454
+ if (overlap === relScalarNames.size && overlap > bestOverlap) {
1455
+ bestOverlap = overlap;
1456
+ bestMatch = entityName;
1457
+ }
1458
+ }
1459
+ if (bestMatch) {
1460
+ rel.entity = bestMatch;
1461
+ }
1462
+ }
1463
+ }
1464
+ return entities;
1465
+ }
1466
+ function unwrapToObjectType(type) {
1467
+ if (isNonNullType(type))
1468
+ return unwrapToObjectType(type.ofType);
1469
+ if (isListType(type))
1470
+ return unwrapToObjectType(type.ofType);
1471
+ if (isObjectType(type))
1472
+ return type;
1473
+ return null;
1474
+ }
1475
+ function unwrapType(type) {
1476
+ let isList = false;
1477
+ let isNonNull = false;
1478
+ let current = type;
1479
+ if (isNonNullType(current)) {
1480
+ isNonNull = true;
1481
+ current = current.ofType;
1482
+ }
1483
+ if (isListType(current)) {
1484
+ isList = true;
1485
+ current = current.ofType;
1486
+ if (isNonNullType(current)) {
1487
+ current = current.ofType;
1488
+ }
1489
+ }
1490
+ return { type: current, isList, isNonNull };
1491
+ }
1492
+ function generateFilterTypeCode(schema, typeName, entities) {
1493
+ const typeMap = schema.getTypeMap();
1494
+ const filterType = typeMap[`${typeName}Filters`];
1495
+ if (!filterType || !isInputObjectType(filterType))
1496
+ return "";
1497
+ const lines = [];
1498
+ const fields = filterType.getFields();
1499
+ lines.push(`export type ${typeName}Filters = {`);
1500
+ for (const [fieldName, field] of Object.entries(fields)) {
1501
+ if (fieldName === "OR") {
1502
+ lines.push(` OR?: ${typeName}Filters[]`);
1503
+ continue;
1504
+ }
1505
+ const entity = entities.get(uncapitalize(typeName));
1506
+ const isRelation = entity?.relations[fieldName];
1507
+ if (isRelation) {
1508
+ const relType = capitalize(isRelation.entity);
1509
+ if (isRelation.type === "one") {
1510
+ lines.push(` ${fieldName}?: ${relType}Filters`);
1511
+ } else {
1512
+ lines.push(` ${fieldName}?: { some?: ${relType}Filters; every?: ${relType}Filters; none?: ${relType}Filters }`);
1513
+ }
1514
+ continue;
1515
+ }
1516
+ const filterInputType = field.type;
1517
+ const unwrapped = isNonNullType(filterInputType) ? filterInputType.ofType : filterInputType;
1518
+ if (isInputObjectType(unwrapped)) {
1519
+ const opFields = unwrapped.getFields();
1520
+ const columnType = inferColumnTsType(opFields);
1521
+ lines.push(` ${fieldName}?: {`);
1522
+ lines.push(` eq?: ${columnType} | null`);
1523
+ lines.push(` ne?: ${columnType} | null`);
1524
+ lines.push(` lt?: ${columnType} | null`);
1525
+ lines.push(` lte?: ${columnType} | null`);
1526
+ lines.push(` gt?: ${columnType} | null`);
1527
+ lines.push(` gte?: ${columnType} | null`);
1528
+ lines.push(` like?: string | null`);
1529
+ lines.push(` notLike?: string | null`);
1530
+ lines.push(` ilike?: string | null`);
1531
+ lines.push(` notIlike?: string | null`);
1532
+ lines.push(` inArray?: ${columnType}[] | null`);
1533
+ lines.push(` notInArray?: ${columnType}[] | null`);
1534
+ lines.push(` isNull?: boolean | null`);
1535
+ lines.push(` isNotNull?: boolean | null`);
1536
+ lines.push(` OR?: Array<Omit<${typeName}Filters['${fieldName}'], 'OR'>> | null`);
1537
+ lines.push(` }`);
1538
+ }
1539
+ }
1540
+ lines.push("}");
1541
+ return lines.join(`
1542
+ `);
1543
+ }
1544
+ function inferColumnTsType(opFields) {
1545
+ const eqField = opFields.eq;
1546
+ if (!eqField)
1547
+ return "unknown";
1548
+ return graphqlTypeToTs(eqField.type);
1549
+ }
1550
+ function graphqlTypeToTs(type) {
1551
+ if (isNonNullType(type))
1552
+ return graphqlTypeToTs(type.ofType);
1553
+ if (isListType(type))
1554
+ return `${graphqlTypeToTs(type.ofType)}[]`;
1555
+ if (isScalarType(type)) {
1556
+ switch (type.name) {
1557
+ case "String":
1558
+ return "string";
1559
+ case "Int":
1560
+ case "Float":
1561
+ return "number";
1562
+ case "Boolean":
1563
+ return "boolean";
1564
+ case "JSON":
1565
+ return "unknown";
1566
+ default:
1567
+ return "unknown";
1568
+ }
1569
+ }
1570
+ if (isEnumType(type)) {
1571
+ return type.getValues().map((v) => `'${v.value}'`).join(" | ");
1572
+ }
1573
+ return "unknown";
1574
+ }
1575
+ function generateInputTypeCode(schema, inputTypeName, exportName) {
1576
+ const typeMap = schema.getTypeMap();
1577
+ const inputType = typeMap[inputTypeName];
1578
+ if (!inputType || !isInputObjectType(inputType))
1579
+ return "";
1580
+ const lines = [];
1581
+ const fields = inputType.getFields();
1582
+ lines.push(`export type ${exportName} = {`);
1583
+ for (const [fieldName, field] of Object.entries(fields)) {
1584
+ const isRequired = isNonNullType(field.type);
1585
+ const tsType = graphqlTypeToTs(field.type);
1586
+ const opt = isRequired ? "" : "?";
1587
+ const nullUnion = isRequired ? "" : " | null";
1588
+ lines.push(` ${fieldName}${opt}: ${tsType}${nullUnion}`);
1589
+ }
1590
+ lines.push("}");
1591
+ return lines.join(`
1592
+ `);
1593
+ }
1594
+ function generateOrderByTypeCode(schema, typeName) {
1595
+ const typeMap = schema.getTypeMap();
1596
+ const orderByType = typeMap[`${typeName}OrderBy`];
1597
+ if (!orderByType || !isInputObjectType(orderByType))
1598
+ return "";
1599
+ const fields = orderByType.getFields();
1600
+ const fieldNames = Object.keys(fields);
1601
+ const lines = [];
1602
+ lines.push(`export type ${typeName}OrderBy = {`);
1603
+ for (const name of fieldNames) {
1604
+ lines.push(` ${name}?: { direction: 'asc' | 'desc'; priority: number }`);
1605
+ }
1606
+ lines.push("}");
1607
+ return lines.join(`
1608
+ `);
1609
+ }
1610
+ function generateWireFormatType(entity, drizzleImportPath, typeNameOverrides) {
1611
+ const drizzleTypeName = typeNameOverrides[entity.tableName] ?? capitalize(entity.tableName);
1612
+ if (drizzleImportPath) {
1613
+ return `export type ${entity.typeName}Wire = Omit<Drizzle${drizzleTypeName}, DateKeys<Drizzle${drizzleTypeName}>>
1614
+ & { [K in DateKeys<Drizzle${drizzleTypeName}>]: string }`;
1615
+ }
1616
+ return `// Wire format for ${entity.tableName} (no Drizzle import configured)
1617
+ export type ${entity.typeName}Wire = Record<string, unknown>`;
1618
+ }
1619
+ function generateSDL(schema) {
1620
+ return printSchema2(schema);
1621
+ }
1622
+ function generateTypes(schema, options) {
1623
+ const entities = getEntitiesFromSchema(schema);
1624
+ const drizzlePath = options?.drizzle?.importPath;
1625
+ const typeOverrides = options?.drizzle?.typeNames ?? {};
1626
+ const lines = [];
1627
+ lines.push("// ─── Auto-generated by drizzle-graphql-pg codegen ────────");
1628
+ lines.push("// Do not edit manually. Re-run the codegen script to update.");
1629
+ lines.push("");
1630
+ lines.push("// biome-ignore lint/suspicious/noExplicitAny: utility type for date key extraction");
1631
+ lines.push("type DateKeys<T> = { [K in keyof T]: T[K] extends Date | null ? K : T[K] extends Date ? K : never }[keyof T]");
1632
+ lines.push("");
1633
+ if (drizzlePath) {
1634
+ const imports = [];
1635
+ for (const entity of entities.values()) {
1636
+ const drizzleTypeName = typeOverrides[entity.tableName] ?? capitalize(entity.tableName);
1637
+ imports.push(` type ${drizzleTypeName} as Drizzle${drizzleTypeName}`);
1638
+ }
1639
+ lines.push(`import {`);
1640
+ lines.push(imports.join(`,
1641
+ `));
1642
+ lines.push(`} from '${drizzlePath}'`);
1643
+ lines.push("");
1644
+ }
1645
+ lines.push("// ─── Wire Format Types ──────────────────────────────────");
1646
+ lines.push("// Drizzle types with Date fields converted to string (GraphQL serialization)");
1647
+ lines.push("");
1648
+ for (const entity of entities.values()) {
1649
+ lines.push(generateWireFormatType(entity, drizzlePath, typeOverrides));
1650
+ lines.push("");
1651
+ }
1652
+ lines.push("// ─── Filter Types ──────────────────────────────────────");
1653
+ lines.push("");
1654
+ for (const entity of entities.values()) {
1655
+ if (!entity.hasFilters)
1656
+ continue;
1657
+ const code = generateFilterTypeCode(schema, entity.typeName, entities);
1658
+ if (code) {
1659
+ lines.push(code);
1660
+ lines.push("");
1661
+ }
1662
+ }
1663
+ lines.push("// ─── Input Types ──────────────────────────────────────");
1664
+ lines.push("");
1665
+ for (const entity of entities.values()) {
1666
+ if (entity.hasInsertInput) {
1667
+ const code = generateInputTypeCode(schema, `${entity.typeName}InsertInput`, `${entity.typeName}InsertInput`);
1668
+ if (code) {
1669
+ lines.push(code);
1670
+ lines.push("");
1671
+ }
1672
+ }
1673
+ if (entity.hasUpdateInput) {
1674
+ const code = generateInputTypeCode(schema, `${entity.typeName}UpdateInput`, `${entity.typeName}UpdateInput`);
1675
+ if (code) {
1676
+ lines.push(code);
1677
+ lines.push("");
1678
+ }
1679
+ }
1680
+ }
1681
+ lines.push("// ─── OrderBy Types ──────────────────────────────────────");
1682
+ lines.push("");
1683
+ for (const entity of entities.values()) {
1684
+ if (!entity.hasOrderBy)
1685
+ continue;
1686
+ const code = generateOrderByTypeCode(schema, entity.typeName);
1687
+ if (code) {
1688
+ lines.push(code);
1689
+ lines.push("");
1690
+ }
1691
+ }
1692
+ return lines.join(`
1693
+ `);
1694
+ }
1695
+ function generateEntityDefs(schema, options) {
1696
+ const entities = getEntitiesFromSchema(schema);
1697
+ const typeOverrides = options?.drizzle?.typeNames ?? {};
1698
+ const lines = [];
1699
+ lines.push("// ─── Auto-generated by drizzle-graphql-pg codegen ────────");
1700
+ lines.push("// Do not edit manually. Re-run the codegen script to update.");
1701
+ lines.push("");
1702
+ lines.push("import type {");
1703
+ const typeImports = [];
1704
+ for (const entity of entities.values()) {
1705
+ typeImports.push(` ${entity.typeName}Wire`);
1706
+ if (entity.hasFilters)
1707
+ typeImports.push(` ${entity.typeName}Filters`);
1708
+ if (entity.hasInsertInput)
1709
+ typeImports.push(` ${entity.typeName}InsertInput`);
1710
+ if (entity.hasUpdateInput)
1711
+ typeImports.push(` ${entity.typeName}UpdateInput`);
1712
+ if (entity.hasOrderBy)
1713
+ typeImports.push(` ${entity.typeName}OrderBy`);
1714
+ }
1715
+ lines.push(typeImports.join(`,
1716
+ `));
1717
+ lines.push("} from './types'");
1718
+ lines.push("");
1719
+ lines.push("export const schema = {");
1720
+ for (const entity of entities.values()) {
1721
+ lines.push(` ${entity.tableName}: {`);
1722
+ if (entity.queryName)
1723
+ lines.push(` queryName: '${entity.queryName}',`);
1724
+ if (entity.queryListName)
1725
+ lines.push(` queryListName: '${entity.queryListName}',`);
1726
+ if (entity.countName)
1727
+ lines.push(` countName: '${entity.countName}',`);
1728
+ if (entity.insertName)
1729
+ lines.push(` insertName: '${entity.insertName}',`);
1730
+ if (entity.insertSingleName)
1731
+ lines.push(` insertSingleName: '${entity.insertSingleName}',`);
1732
+ if (entity.updateName)
1733
+ lines.push(` updateName: '${entity.updateName}',`);
1734
+ if (entity.deleteName)
1735
+ lines.push(` deleteName: '${entity.deleteName}',`);
1736
+ lines.push(` fields: [${entity.fields.map((f) => `'${f}'`).join(", ")}],`);
1737
+ lines.push(` relations: {`);
1738
+ for (const [relName, rel] of Object.entries(entity.relations)) {
1739
+ lines.push(` ${relName}: { entity: '${rel.entity}', type: '${rel.type}' },`);
1740
+ }
1741
+ lines.push(` },`);
1742
+ lines.push(` },`);
1743
+ }
1744
+ lines.push("} as const");
1745
+ lines.push("");
1746
+ lines.push("export type EntityDefs = {");
1747
+ for (const entity of entities.values()) {
1748
+ lines.push(` ${entity.tableName}: {`);
1749
+ lines.push(` fields: ${entity.typeName}Wire`);
1750
+ lines.push(` relations: {`);
1751
+ for (const [relName, rel] of Object.entries(entity.relations)) {
1752
+ lines.push(` ${relName}: { entity: '${rel.entity}'; type: '${rel.type}' }`);
1753
+ }
1754
+ lines.push(` }`);
1755
+ if (entity.hasFilters)
1756
+ lines.push(` filters: ${entity.typeName}Filters`);
1757
+ if (entity.hasInsertInput)
1758
+ lines.push(` insertInput: ${entity.typeName}InsertInput`);
1759
+ if (entity.hasUpdateInput)
1760
+ lines.push(` updateInput: ${entity.typeName}UpdateInput`);
1761
+ if (entity.hasOrderBy)
1762
+ lines.push(` orderBy: ${entity.typeName}OrderBy`);
1763
+ lines.push(` }`);
1764
+ }
1765
+ lines.push("}");
1766
+ lines.push("");
1767
+ lines.push("export type TableNameMap = {");
1768
+ for (const entity of entities.values()) {
1769
+ const drizzleTypeName = typeOverrides[entity.tableName] ?? capitalize(entity.tableName);
1770
+ lines.push(` ${drizzleTypeName}: '${entity.tableName}'`);
1771
+ }
1772
+ lines.push("}");
1773
+ lines.push("");
1774
+ return lines.join(`
1775
+ `);
1776
+ }
1777
+
1778
+ // src/index.ts
1779
+ var buildSchema = (db, config) => {
1780
+ const builder = new SchemaBuilder(db, config);
1781
+ return builder.build();
1782
+ };
1783
+ var buildEntities = (db, config) => {
1784
+ const builder = new SchemaBuilder(db, config);
1785
+ return builder.buildEntities();
1786
+ };
1787
+ var buildSchemaFromDrizzle = (drizzleSchema, config) => {
1788
+ const { tables, tableNamesMap } = extractTablesRelationalConfig(drizzleSchema, createTableRelationsHelpers2);
1789
+ const schemaKeys = Object.keys(tables);
1790
+ const findStub = {
1791
+ findMany: () => Promise.resolve([]),
1792
+ findFirst: () => Promise.resolve(null)
1793
+ };
1794
+ const query = Object.fromEntries(schemaKeys.map((name) => [name, findStub]));
1795
+ const mockDb = {
1796
+ _: { fullSchema: drizzleSchema, schema: tables, tableNamesMap },
1797
+ query,
1798
+ select: () => ({ from: () => ({ where: () => ({}) }) })
1799
+ };
1800
+ const builder = new SchemaBuilder(mockDb, config);
1801
+ return builder.build();
1802
+ };
1803
+ export {
1804
+ generateTypes,
1805
+ generateSDL,
1806
+ generateEntityDefs,
1807
+ buildSchemaFromDrizzle,
1808
+ buildSchema,
1809
+ buildEntities,
1810
+ SchemaBuilder,
1811
+ GraphQLJSON
1812
+ };