@constructive-io/graphql-codegen 3.3.2 → 4.0.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.
Files changed (67) hide show
  1. package/README.md +0 -4
  2. package/core/ast.js +6 -5
  3. package/core/codegen/custom-mutations.js +22 -22
  4. package/core/codegen/custom-queries.js +24 -17
  5. package/core/codegen/hooks-ast.d.ts +1 -1
  6. package/core/codegen/hooks-ast.js +22 -5
  7. package/core/codegen/index.d.ts +1 -1
  8. package/core/codegen/mutations.js +16 -12
  9. package/core/codegen/orm/custom-ops-generator.js +37 -3
  10. package/core/codegen/orm/input-types-generator.js +161 -89
  11. package/core/codegen/orm/model-generator.js +72 -73
  12. package/core/codegen/orm/select-types.d.ts +27 -17
  13. package/core/codegen/queries.js +37 -25
  14. package/core/codegen/schema-types-generator.js +21 -0
  15. package/core/codegen/templates/hooks-selection.ts +12 -0
  16. package/core/codegen/templates/query-builder.ts +103 -59
  17. package/core/codegen/templates/select-types.ts +59 -33
  18. package/core/codegen/types.js +26 -0
  19. package/core/codegen/utils.d.ts +1 -1
  20. package/core/codegen/utils.js +1 -1
  21. package/core/custom-ast.js +9 -8
  22. package/core/database/index.js +2 -3
  23. package/core/index.d.ts +2 -0
  24. package/core/index.js +2 -0
  25. package/core/introspect/infer-tables.js +144 -58
  26. package/core/introspect/transform-schema.d.ts +1 -1
  27. package/core/introspect/transform-schema.js +3 -1
  28. package/esm/core/ast.js +6 -5
  29. package/esm/core/codegen/custom-mutations.js +23 -23
  30. package/esm/core/codegen/custom-queries.js +25 -18
  31. package/esm/core/codegen/hooks-ast.d.ts +1 -1
  32. package/esm/core/codegen/hooks-ast.js +22 -5
  33. package/esm/core/codegen/index.d.ts +1 -1
  34. package/esm/core/codegen/mutations.js +16 -12
  35. package/esm/core/codegen/orm/custom-ops-generator.js +38 -4
  36. package/esm/core/codegen/orm/input-types-generator.js +163 -91
  37. package/esm/core/codegen/orm/model-generator.js +73 -74
  38. package/esm/core/codegen/orm/select-types.d.ts +27 -17
  39. package/esm/core/codegen/queries.js +37 -25
  40. package/esm/core/codegen/schema-types-generator.js +21 -0
  41. package/esm/core/codegen/types.js +26 -0
  42. package/esm/core/codegen/utils.d.ts +1 -1
  43. package/esm/core/codegen/utils.js +1 -1
  44. package/esm/core/custom-ast.js +9 -8
  45. package/esm/core/database/index.js +2 -3
  46. package/esm/core/index.d.ts +2 -0
  47. package/esm/core/index.js +2 -0
  48. package/esm/core/introspect/infer-tables.js +144 -58
  49. package/esm/core/introspect/transform-schema.d.ts +1 -1
  50. package/esm/core/introspect/transform-schema.js +3 -1
  51. package/esm/generators/field-selector.js +1 -0
  52. package/esm/generators/index.d.ts +3 -0
  53. package/esm/generators/index.js +3 -0
  54. package/esm/generators/mutations.js +4 -4
  55. package/esm/generators/select.js +4 -4
  56. package/esm/index.d.ts +1 -1
  57. package/esm/index.js +1 -1
  58. package/esm/types/schema.d.ts +5 -3
  59. package/generators/field-selector.js +1 -0
  60. package/generators/index.d.ts +3 -0
  61. package/generators/index.js +3 -0
  62. package/generators/mutations.js +3 -3
  63. package/generators/select.js +3 -3
  64. package/index.d.ts +1 -1
  65. package/index.js +1 -1
  66. package/package.json +10 -10
  67. package/types/schema.d.ts +5 -3
@@ -365,6 +365,41 @@ function generateEnumTypes(typeRegistry, enumTypeNames) {
365
365
  }
366
366
  return statements;
367
367
  }
368
+ function collectCustomScalarTypes(typeRegistry, tables, excludedTypeNames) {
369
+ const customScalarTypes = new Set();
370
+ const tableTypeNames = new Set(tables.map((table) => table.name));
371
+ for (const [typeName, typeInfo] of typeRegistry) {
372
+ if (typeInfo.kind !== 'SCALAR')
373
+ continue;
374
+ if (scalars_1.SCALAR_NAMES.has(typeName))
375
+ continue;
376
+ if (excludedTypeNames.has(typeName))
377
+ continue;
378
+ customScalarTypes.add(typeName);
379
+ }
380
+ for (const table of tables) {
381
+ for (const field of table.fields) {
382
+ const fieldType = typeof field.type === 'string' ? field.type : field.type.gqlType;
383
+ if (scalars_1.SCALAR_NAMES.has(fieldType))
384
+ continue;
385
+ if (excludedTypeNames.has(fieldType))
386
+ continue;
387
+ if (tableTypeNames.has(fieldType))
388
+ continue;
389
+ if (isLikelyEnumType(fieldType, typeRegistry))
390
+ continue;
391
+ customScalarTypes.add(fieldType);
392
+ }
393
+ }
394
+ return Array.from(customScalarTypes).sort();
395
+ }
396
+ function generateCustomScalarTypes(customScalarTypes) {
397
+ if (customScalarTypes.length === 0)
398
+ return [];
399
+ const statements = customScalarTypes.map((scalarType) => createExportedTypeAlias(scalarType, 'unknown'));
400
+ addSectionComment(statements, 'Custom Scalar Types');
401
+ return statements;
402
+ }
368
403
  // ============================================================================
369
404
  // Entity Types Generator (AST-based)
370
405
  // ============================================================================
@@ -546,15 +581,12 @@ function buildSelectTypeLiteral(table, tableByName) {
546
581
  for (const relation of table.relations.belongsTo) {
547
582
  if (relation.fieldName) {
548
583
  const relatedTypeName = getRelatedTypeName(relation.referencesTable, tableByName);
549
- const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsUnionType([
550
- t.tsBooleanKeyword(),
551
- t.tsTypeLiteral([
552
- (() => {
553
- const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
554
- selectProp.optional = true;
555
- return selectProp;
556
- })(),
557
- ]),
584
+ const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsTypeLiteral([
585
+ (() => {
586
+ const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
587
+ selectProp.optional = false;
588
+ return selectProp;
589
+ })(),
558
590
  ])));
559
591
  prop.optional = true;
560
592
  members.push(prop);
@@ -566,30 +598,27 @@ function buildSelectTypeLiteral(table, tableByName) {
566
598
  const relatedTypeName = getRelatedTypeName(relation.referencedByTable, tableByName);
567
599
  const filterName = getRelatedFilterName(relation.referencedByTable, tableByName);
568
600
  const orderByName = getRelatedOrderByName(relation.referencedByTable, tableByName);
569
- const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsUnionType([
570
- t.tsBooleanKeyword(),
571
- t.tsTypeLiteral([
572
- (() => {
573
- const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
574
- p.optional = true;
575
- return p;
576
- })(),
577
- (() => {
578
- const p = t.tsPropertySignature(t.identifier('first'), t.tsTypeAnnotation(t.tsNumberKeyword()));
579
- p.optional = true;
580
- return p;
581
- })(),
582
- (() => {
583
- const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))));
584
- p.optional = true;
585
- return p;
586
- })(),
587
- (() => {
588
- const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByName)))));
589
- p.optional = true;
590
- return p;
591
- })(),
592
- ]),
601
+ const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsTypeLiteral([
602
+ (() => {
603
+ const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
604
+ p.optional = false;
605
+ return p;
606
+ })(),
607
+ (() => {
608
+ const p = t.tsPropertySignature(t.identifier('first'), t.tsTypeAnnotation(t.tsNumberKeyword()));
609
+ p.optional = true;
610
+ return p;
611
+ })(),
612
+ (() => {
613
+ const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))));
614
+ p.optional = true;
615
+ return p;
616
+ })(),
617
+ (() => {
618
+ const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByName)))));
619
+ p.optional = true;
620
+ return p;
621
+ })(),
593
622
  ])));
594
623
  prop.optional = true;
595
624
  members.push(prop);
@@ -601,30 +630,27 @@ function buildSelectTypeLiteral(table, tableByName) {
601
630
  const relatedTypeName = getRelatedTypeName(relation.rightTable, tableByName);
602
631
  const filterName = getRelatedFilterName(relation.rightTable, tableByName);
603
632
  const orderByName = getRelatedOrderByName(relation.rightTable, tableByName);
604
- const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsUnionType([
605
- t.tsBooleanKeyword(),
606
- t.tsTypeLiteral([
607
- (() => {
608
- const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
609
- p.optional = true;
610
- return p;
611
- })(),
612
- (() => {
613
- const p = t.tsPropertySignature(t.identifier('first'), t.tsTypeAnnotation(t.tsNumberKeyword()));
614
- p.optional = true;
615
- return p;
616
- })(),
617
- (() => {
618
- const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))));
619
- p.optional = true;
620
- return p;
621
- })(),
622
- (() => {
623
- const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByName)))));
624
- p.optional = true;
625
- return p;
626
- })(),
627
- ]),
633
+ const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsTypeLiteral([
634
+ (() => {
635
+ const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
636
+ p.optional = false;
637
+ return p;
638
+ })(),
639
+ (() => {
640
+ const p = t.tsPropertySignature(t.identifier('first'), t.tsTypeAnnotation(t.tsNumberKeyword()));
641
+ p.optional = true;
642
+ return p;
643
+ })(),
644
+ (() => {
645
+ const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))));
646
+ p.optional = true;
647
+ return p;
648
+ })(),
649
+ (() => {
650
+ const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByName)))));
651
+ p.optional = true;
652
+ return p;
653
+ })(),
628
654
  ])));
629
655
  prop.optional = true;
630
656
  members.push(prop);
@@ -634,15 +660,12 @@ function buildSelectTypeLiteral(table, tableByName) {
634
660
  for (const relation of table.relations.hasOne) {
635
661
  if (relation.fieldName) {
636
662
  const relatedTypeName = getRelatedTypeName(relation.referencedByTable, tableByName);
637
- const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsUnionType([
638
- t.tsBooleanKeyword(),
639
- t.tsTypeLiteral([
640
- (() => {
641
- const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
642
- selectProp.optional = true;
643
- return selectProp;
644
- })(),
645
- ]),
663
+ const prop = t.tsPropertySignature(t.identifier(relation.fieldName), t.tsTypeAnnotation(t.tsTypeLiteral([
664
+ (() => {
665
+ const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${relatedTypeName}Select`))));
666
+ selectProp.optional = false;
667
+ return selectProp;
668
+ })(),
646
669
  ])));
647
670
  prop.optional = true;
648
671
  members.push(prop);
@@ -784,7 +807,7 @@ function generateOrderByTypes(tables) {
784
807
  /**
785
808
  * Build the nested data object fields for Create input
786
809
  */
787
- function buildCreateDataFields(table) {
810
+ function buildCreateDataFieldsFromTable(table) {
788
811
  const fields = [];
789
812
  for (const field of table.fields) {
790
813
  if (EXCLUDED_MUTATION_FIELDS.includes(field.name))
@@ -798,12 +821,60 @@ function buildCreateDataFields(table) {
798
821
  }
799
822
  return fields;
800
823
  }
824
+ /**
825
+ * Build Create input fields from schema Input types when available.
826
+ *
827
+ * This follows the actual GraphQL input shape:
828
+ * `CreateXInput { clientMutationId?, x: XInput! }`
829
+ */
830
+ function buildCreateDataFieldsFromSchema(table, typeRegistry) {
831
+ const createInputTypeName = (0, utils_1.getCreateInputTypeName)(table);
832
+ const createInputType = typeRegistry.get(createInputTypeName);
833
+ if (!createInputType ||
834
+ createInputType.kind !== 'INPUT_OBJECT' ||
835
+ !createInputType.inputFields) {
836
+ return null;
837
+ }
838
+ const { singularName } = (0, utils_1.getTableNames)(table);
839
+ const entityArg = createInputType.inputFields.find((field) => field.name === singularName) ??
840
+ createInputType.inputFields.find((field) => field.name !== 'clientMutationId');
841
+ if (!entityArg)
842
+ return null;
843
+ const entityInputTypeName = (0, type_resolver_1.getTypeBaseName)(entityArg.type);
844
+ if (!entityInputTypeName)
845
+ return null;
846
+ const entityInputType = typeRegistry.get(entityInputTypeName);
847
+ if (!entityInputType ||
848
+ entityInputType.kind !== 'INPUT_OBJECT' ||
849
+ !entityInputType.inputFields) {
850
+ return null;
851
+ }
852
+ const fields = [];
853
+ for (const field of entityInputType.inputFields) {
854
+ if (EXCLUDED_MUTATION_FIELDS.includes(field.name)) {
855
+ continue;
856
+ }
857
+ fields.push({
858
+ name: field.name,
859
+ type: typeRefToTs(field.type),
860
+ optional: !isRequired(field.type),
861
+ });
862
+ }
863
+ return fields;
864
+ }
865
+ /**
866
+ * Build Create input fields, preferring schema-derived Input object fields.
867
+ */
868
+ function buildCreateDataFields(table, typeRegistry) {
869
+ return (buildCreateDataFieldsFromSchema(table, typeRegistry) ??
870
+ buildCreateDataFieldsFromTable(table));
871
+ }
801
872
  /**
802
873
  * Build Create input interface as AST
803
874
  */
804
- function buildCreateInputInterface(table) {
875
+ function buildCreateInputInterface(table, typeRegistry) {
805
876
  const { typeName, singularName } = (0, utils_1.getTableNames)(table);
806
- const fields = buildCreateDataFields(table);
877
+ const fields = buildCreateDataFields(table, typeRegistry);
807
878
  // Build the nested object type for the entity data
808
879
  const nestedProps = fields.map((field) => {
809
880
  const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(parseTypeString(field.type)));
@@ -847,7 +918,7 @@ function buildPatchProperties(table) {
847
918
  /**
848
919
  * Generate CRUD input type statements for a table
849
920
  */
850
- function generateCrudInputTypes(table) {
921
+ function generateCrudInputTypes(table, typeRegistry) {
851
922
  const statements = [];
852
923
  const { typeName } = (0, utils_1.getTableNames)(table);
853
924
  const patchName = `${typeName}Patch`;
@@ -856,14 +927,15 @@ function generateCrudInputTypes(table) {
856
927
  const pkFieldName = pkField?.name ?? 'id';
857
928
  const pkFieldTsType = pkField?.tsType ?? 'string';
858
929
  // Create input
859
- statements.push(buildCreateInputInterface(table));
930
+ statements.push(buildCreateInputInterface(table, typeRegistry));
860
931
  // Patch interface
861
932
  statements.push(createExportedInterface(patchName, buildPatchProperties(table)));
862
- // Update input
933
+ // Update input - v5 uses entity-specific patch field names (e.g., "userPatch")
934
+ const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
863
935
  statements.push(createExportedInterface(`Update${typeName}Input`, [
864
936
  { name: 'clientMutationId', type: 'string', optional: true },
865
937
  { name: pkFieldName, type: pkFieldTsType, optional: false },
866
- { name: 'patch', type: patchName, optional: false },
938
+ { name: patchFieldName, type: patchName, optional: false },
867
939
  ]));
868
940
  // Delete input
869
941
  statements.push(createExportedInterface(`Delete${typeName}Input`, [
@@ -875,10 +947,10 @@ function generateCrudInputTypes(table) {
875
947
  /**
876
948
  * Generate all CRUD input type statements
877
949
  */
878
- function generateAllCrudInputTypes(tables) {
950
+ function generateAllCrudInputTypes(tables, typeRegistry) {
879
951
  const statements = [];
880
952
  for (const table of tables) {
881
- statements.push(...generateCrudInputTypes(table));
953
+ statements.push(...generateCrudInputTypes(table, typeRegistry));
882
954
  }
883
955
  if (statements.length > 0) {
884
956
  addSectionComment(statements, 'CRUD Input Types');
@@ -1081,15 +1153,12 @@ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTy
1081
1153
  const nestedType = baseType ? typeRegistry.get(baseType) : null;
1082
1154
  let propType;
1083
1155
  if (nestedType?.kind === 'OBJECT') {
1084
- propType = t.tsUnionType([
1085
- t.tsBooleanKeyword(),
1086
- t.tsTypeLiteral([
1087
- (() => {
1088
- const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${baseType}Select`))));
1089
- p.optional = true;
1090
- return p;
1091
- })(),
1092
- ]),
1156
+ propType = t.tsTypeLiteral([
1157
+ (() => {
1158
+ const p = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${baseType}Select`))));
1159
+ p.optional = false;
1160
+ return p;
1161
+ })(),
1093
1162
  ]);
1094
1163
  }
1095
1164
  else {
@@ -1160,13 +1229,16 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
1160
1229
  const tablesList = tables ?? [];
1161
1230
  const hasTables = tablesList.length > 0;
1162
1231
  const tableByName = new Map(tablesList.map((table) => [table.name, table]));
1232
+ const enumTypes = hasTables
1233
+ ? collectEnumTypesFromTables(tablesList, typeRegistry)
1234
+ : new Set();
1235
+ const customScalarTypes = collectCustomScalarTypes(typeRegistry, tablesList, enumTypes);
1163
1236
  // 1. Scalar filter types
1164
1237
  statements.push(...generateScalarFilterTypes());
1165
1238
  // 2. Enum types used by table fields
1166
- if (hasTables) {
1167
- const enumTypes = collectEnumTypesFromTables(tablesList, typeRegistry);
1168
- statements.push(...generateEnumTypes(typeRegistry, enumTypes));
1169
- }
1239
+ statements.push(...generateEnumTypes(typeRegistry, enumTypes));
1240
+ // 2b. Unknown/custom scalar aliases for schema-specific scalars
1241
+ statements.push(...generateCustomScalarTypes(customScalarTypes));
1170
1242
  // 3. Entity and relation types (if tables provided)
1171
1243
  if (hasTables) {
1172
1244
  statements.push(...generateEntityTypes(tablesList));
@@ -1181,7 +1253,7 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
1181
1253
  // 5. OrderBy types
1182
1254
  statements.push(...generateOrderByTypes(tablesList));
1183
1255
  // 6. CRUD input types
1184
- statements.push(...generateAllCrudInputTypes(tablesList));
1256
+ statements.push(...generateAllCrudInputTypes(tablesList, typeRegistry));
1185
1257
  }
1186
1258
  // 6b. Connection fields map (runtime metadata for buildSelections)
1187
1259
  // Always emit this export so generated model/custom-op imports stay valid.
@@ -50,7 +50,7 @@ function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false
50
50
  decl.importKind = typeOnly ? 'type' : 'value';
51
51
  return decl;
52
52
  }
53
- function buildMethodBody(builderFn, args, operation, typeName, fieldName) {
53
+ function buildMethodBody(builderFn, args, operation, typeName, fieldName, extraProps = []) {
54
54
  const destructureDecl = t.variableDeclaration('const', [
55
55
  t.variableDeclarator(t.objectPattern([
56
56
  t.objectProperty(t.identifier('document'), t.identifier('document'), false, true),
@@ -65,6 +65,7 @@ function buildMethodBody(builderFn, args, operation, typeName, fieldName) {
65
65
  t.objectProperty(t.identifier('fieldName'), t.stringLiteral(fieldName)),
66
66
  t.objectProperty(t.identifier('document'), t.identifier('document'), false, true),
67
67
  t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true),
68
+ ...extraProps,
68
69
  ]),
69
70
  ]));
70
71
  return [destructureDecl, returnStmt];
@@ -75,10 +76,6 @@ function createClassMethod(name, typeParameters, params, returnType, body) {
75
76
  method.returnType = returnType;
76
77
  return method;
77
78
  }
78
- function createDeclareMethod(name, typeParameters, params, returnType) {
79
- const method = t.tsDeclareMethod(null, t.identifier(name), typeParameters, params, returnType);
80
- return method;
81
- }
82
79
  function createTypeParam(constraintTypeName, defaultType) {
83
80
  const param = t.tsTypeParameter(t.tsTypeReference(t.identifier(constraintTypeName)), defaultType ?? null, 'S');
84
81
  return t.tsTypeParameterDeclaration([param]);
@@ -122,6 +119,8 @@ function generateModelFile(table, _useSharedTypes) {
122
119
  const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
123
120
  const pkField = pkFields[0];
124
121
  const pluralQueryName = table.query?.all ?? pluralName;
122
+ const singleQueryName = table.query?.one;
123
+ const singleResultFieldName = (0, utils_1.getSingleRowQueryName)(table);
125
124
  const createMutationName = table.query?.create ?? `create${typeName}`;
126
125
  const updateMutationName = table.query?.update;
127
126
  const deleteMutationName = table.query?.delete;
@@ -166,7 +165,6 @@ function generateModelFile(table, _useSharedTypes) {
166
165
  classBody.push(t.classMethod('constructor', t.identifier('constructor'), [paramProp], t.blockStatement([])));
167
166
  // Reusable type reference factories
168
167
  const sRef = () => t.tsTypeReference(t.identifier('S'));
169
- const selectRef = () => t.tsTypeReference(t.identifier(selectTypeName));
170
168
  const pkTsType = () => tsTypeFromPrimitive(pkField.tsType);
171
169
  // ── findMany ───────────────────────────────────────────────────────────
172
170
  {
@@ -185,17 +183,12 @@ function generateModelFile(table, _useSharedTypes) {
185
183
  ])))),
186
184
  ]),
187
185
  ])));
188
- // Overload 1: with select (autocompletion)
189
- const o1Param = t.identifier('args');
190
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
186
+ const implParam = t.identifier('args');
187
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
191
188
  argsType(sRef()),
192
189
  t.tsTypeLiteral([requiredSelectProp()]),
193
190
  strictSelectGuard(selectTypeName),
194
191
  ]));
195
- classBody.push(createDeclareMethod('findMany', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
196
- // Implementation
197
- const implParam = t.identifier('args');
198
- implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
199
192
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
200
193
  const bodyArgs = [
201
194
  t.stringLiteral(typeName),
@@ -217,7 +210,7 @@ function generateModelFile(table, _useSharedTypes) {
217
210
  t.stringLiteral(orderByTypeName),
218
211
  t.identifier('connectionFieldsMap'),
219
212
  ];
220
- classBody.push(createClassMethod('findMany', null, [implParam], null, buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName)));
213
+ classBody.push(createClassMethod('findMany', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName)));
221
214
  }
222
215
  // ── findFirst ──────────────────────────────────────────────────────────
223
216
  {
@@ -235,17 +228,12 @@ function generateModelFile(table, _useSharedTypes) {
235
228
  ]))),
236
229
  ]),
237
230
  ])));
238
- // Overload 1: with select (autocompletion)
239
- const o1Param = t.identifier('args');
240
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
231
+ const implParam = t.identifier('args');
232
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
241
233
  argsType(sRef()),
242
234
  t.tsTypeLiteral([requiredSelectProp()]),
243
235
  strictSelectGuard(selectTypeName),
244
236
  ]));
245
- classBody.push(createDeclareMethod('findFirst', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
246
- // Implementation
247
- const implParam = t.identifier('args');
248
- implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
249
237
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
250
238
  const bodyArgs = [
251
239
  t.stringLiteral(typeName),
@@ -257,15 +245,13 @@ function generateModelFile(table, _useSharedTypes) {
257
245
  t.stringLiteral(whereTypeName),
258
246
  t.identifier('connectionFieldsMap'),
259
247
  ];
260
- classBody.push(createClassMethod('findFirst', null, [implParam], null, buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName)));
248
+ classBody.push(createClassMethod('findFirst', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName)));
261
249
  }
262
250
  // ── findOne ────────────────────────────────────────────────────────────
263
- const singleQueryName = table.query?.one;
264
- if (singleQueryName && (0, utils_1.hasValidPrimaryKey)(table)) {
265
- const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!';
251
+ if ((0, utils_1.hasValidPrimaryKey)(table)) {
266
252
  const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
267
253
  t.tsTypeLiteral([
268
- t.tsPropertySignature(t.identifier(singleQueryName), t.tsTypeAnnotation(t.tsUnionType([
254
+ t.tsPropertySignature(t.identifier(singleResultFieldName), t.tsTypeAnnotation(t.tsUnionType([
269
255
  t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
270
256
  t.tsTypeReference(t.identifier(relationTypeName)),
271
257
  sel,
@@ -279,33 +265,59 @@ function generateModelFile(table, _useSharedTypes) {
279
265
  prop.optional = false;
280
266
  return prop;
281
267
  };
282
- // Overload 1: with select (autocompletion)
283
- const o1Param = t.identifier('args');
284
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
268
+ const implParam = t.identifier('args');
269
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
285
270
  t.tsTypeLiteral([pkProp(), requiredSelectProp()]),
286
271
  strictSelectGuard(selectTypeName),
287
272
  ]));
288
- classBody.push(createDeclareMethod('findOne', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
289
- // Implementation
290
- const implParam = t.identifier('args');
291
- implParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([
292
- pkProp(),
293
- (() => {
294
- const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))));
295
- return prop;
296
- })(),
297
- ]));
298
273
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
299
- const bodyArgs = [
300
- t.stringLiteral(typeName),
301
- t.stringLiteral(singleQueryName),
302
- t.memberExpression(t.identifier('args'), t.identifier(pkField.name)),
303
- selectExpr,
304
- t.stringLiteral(pkField.name),
305
- t.stringLiteral(pkGqlType),
306
- t.identifier('connectionFieldsMap'),
307
- ];
308
- classBody.push(createClassMethod('findOne', null, [implParam], null, buildMethodBody('buildFindOneDocument', bodyArgs, 'query', typeName, singleQueryName)));
274
+ if (singleQueryName) {
275
+ const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!';
276
+ const bodyArgs = [
277
+ t.stringLiteral(typeName),
278
+ t.stringLiteral(singleQueryName),
279
+ t.memberExpression(t.identifier('args'), t.identifier(pkField.name)),
280
+ selectExpr,
281
+ t.stringLiteral(pkField.name),
282
+ t.stringLiteral(pkGqlType),
283
+ t.identifier('connectionFieldsMap'),
284
+ ];
285
+ classBody.push(createClassMethod('findOne', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindOneDocument', bodyArgs, 'query', typeName, singleResultFieldName)));
286
+ }
287
+ else {
288
+ const idExpr = t.memberExpression(t.identifier('args'), t.identifier(pkField.name));
289
+ const findOneArgs = t.objectExpression([
290
+ t.objectProperty(t.identifier('where'), t.objectExpression([
291
+ t.objectProperty(t.identifier(pkField.name), t.objectExpression([
292
+ t.objectProperty(t.identifier('equalTo'), idExpr),
293
+ ])),
294
+ ])),
295
+ t.objectProperty(t.identifier('first'), t.numericLiteral(1)),
296
+ ]);
297
+ const bodyArgs = [
298
+ t.stringLiteral(typeName),
299
+ t.stringLiteral(pluralQueryName),
300
+ selectExpr,
301
+ findOneArgs,
302
+ t.stringLiteral(whereTypeName),
303
+ t.stringLiteral(orderByTypeName),
304
+ t.identifier('connectionFieldsMap'),
305
+ ];
306
+ const firstNodeExpr = t.optionalMemberExpression(t.optionalMemberExpression(t.memberExpression(t.identifier('data'), t.identifier(pluralQueryName)), t.identifier('nodes'), false, true), t.numericLiteral(0), true, true);
307
+ const transformDataParam = t.identifier('data');
308
+ const transformedNodesProp = t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
309
+ t.tsTypeReference(t.identifier(relationTypeName)),
310
+ sRef(),
311
+ ])))));
312
+ transformedNodesProp.optional = true;
313
+ const transformedCollectionProp = t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation(t.tsTypeLiteral([transformedNodesProp])));
314
+ transformedCollectionProp.optional = true;
315
+ transformDataParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([transformedCollectionProp]));
316
+ const transformFn = t.arrowFunctionExpression([transformDataParam], t.objectExpression([
317
+ t.objectProperty(t.stringLiteral(singleResultFieldName), t.logicalExpression('??', firstNodeExpr, t.nullLiteral())),
318
+ ]));
319
+ classBody.push(createClassMethod('findOne', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, singleResultFieldName, [t.objectProperty(t.identifier('transform'), transformFn)])));
320
+ }
309
321
  }
310
322
  // ── create ─────────────────────────────────────────────────────────────
311
323
  {
@@ -321,17 +333,12 @@ function generateModelFile(table, _useSharedTypes) {
321
333
  ]))),
322
334
  ]),
323
335
  ])));
324
- // Overload 1: with select (autocompletion)
325
- const o1Param = t.identifier('args');
326
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
336
+ const implParam = t.identifier('args');
337
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
327
338
  argsType(sRef()),
328
339
  t.tsTypeLiteral([requiredSelectProp()]),
329
340
  strictSelectGuard(selectTypeName),
330
341
  ]));
331
- classBody.push(createDeclareMethod('create', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
332
- // Implementation
333
- const implParam = t.identifier('args');
334
- implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
335
342
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
336
343
  const bodyArgs = [
337
344
  t.stringLiteral(typeName),
@@ -342,7 +349,7 @@ function generateModelFile(table, _useSharedTypes) {
342
349
  t.stringLiteral(createInputTypeName),
343
350
  t.identifier('connectionFieldsMap'),
344
351
  ];
345
- classBody.push(createClassMethod('create', null, [implParam], null, buildMethodBody('buildCreateDocument', bodyArgs, 'mutation', typeName, createMutationName)));
352
+ classBody.push(createClassMethod('create', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildCreateDocument', bodyArgs, 'mutation', typeName, createMutationName)));
346
353
  }
347
354
  // ── update ─────────────────────────────────────────────────────────────
348
355
  if (updateMutationName) {
@@ -368,18 +375,14 @@ function generateModelFile(table, _useSharedTypes) {
368
375
  ]))),
369
376
  ]),
370
377
  ])));
371
- // Overload 1: with select (autocompletion)
372
- const o1Param = t.identifier('args');
373
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
378
+ const implParam = t.identifier('args');
379
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
374
380
  argsType(sRef()),
375
381
  t.tsTypeLiteral([requiredSelectProp()]),
376
382
  strictSelectGuard(selectTypeName),
377
383
  ]));
378
- classBody.push(createDeclareMethod('update', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
379
- // Implementation
380
- const implParam = t.identifier('args');
381
- implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
382
384
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
385
+ const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
383
386
  const bodyArgs = [
384
387
  t.stringLiteral(typeName),
385
388
  t.stringLiteral(updateMutationName),
@@ -389,9 +392,10 @@ function generateModelFile(table, _useSharedTypes) {
389
392
  t.memberExpression(t.identifier('args'), t.identifier('data')),
390
393
  t.stringLiteral(updateInputTypeName),
391
394
  t.stringLiteral(pkField.name),
395
+ t.stringLiteral(patchFieldName),
392
396
  t.identifier('connectionFieldsMap'),
393
397
  ];
394
- classBody.push(createClassMethod('update', null, [implParam], null, buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName)));
398
+ classBody.push(createClassMethod('update', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName)));
395
399
  }
396
400
  // ── delete ─────────────────────────────────────────────────────────────
397
401
  if (deleteMutationName) {
@@ -413,17 +417,12 @@ function generateModelFile(table, _useSharedTypes) {
413
417
  ]))),
414
418
  ]),
415
419
  ])));
416
- // Overload 1: with select (autocompletion)
417
- const o1Param = t.identifier('args');
418
- o1Param.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
420
+ const implParam = t.identifier('args');
421
+ implParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
419
422
  argsType(sRef()),
420
423
  t.tsTypeLiteral([requiredSelectProp()]),
421
424
  strictSelectGuard(selectTypeName),
422
425
  ]));
423
- classBody.push(createDeclareMethod('delete', createTypeParam(selectTypeName), [o1Param], retType(sRef())));
424
- // Implementation
425
- const implParam = t.identifier('args');
426
- implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef()));
427
426
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
428
427
  const bodyArgs = [
429
428
  t.stringLiteral(typeName),
@@ -435,7 +434,7 @@ function generateModelFile(table, _useSharedTypes) {
435
434
  selectExpr,
436
435
  t.identifier('connectionFieldsMap'),
437
436
  ];
438
- classBody.push(createClassMethod('delete', null, [implParam], null, buildMethodBody('buildDeleteByPkDocument', bodyArgs, 'mutation', typeName, deleteMutationName)));
437
+ classBody.push(createClassMethod('delete', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildDeleteByPkDocument', bodyArgs, 'mutation', typeName, deleteMutationName)));
439
438
  }
440
439
  const classDecl = t.classDeclaration(t.identifier(modelName), null, t.classBody(classBody));
441
440
  statements.push(t.exportNamedDeclaration(classDecl));