@gqlkit-ts/cli 0.2.0 → 0.3.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 (190) hide show
  1. package/dist/auto-type-generator/auto-type-generator.d.ts +10 -4
  2. package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
  3. package/dist/auto-type-generator/auto-type-generator.js +640 -133
  4. package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
  5. package/dist/auto-type-generator/index.d.ts +8 -1
  6. package/dist/auto-type-generator/index.d.ts.map +1 -1
  7. package/dist/auto-type-generator/index.js +3 -0
  8. package/dist/auto-type-generator/index.js.map +1 -1
  9. package/dist/auto-type-generator/inline-enum-collector.d.ts +13 -5
  10. package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -1
  11. package/dist/auto-type-generator/inline-enum-collector.js +107 -71
  12. package/dist/auto-type-generator/inline-enum-collector.js.map +1 -1
  13. package/dist/auto-type-generator/inline-object-traverser.d.ts +20 -0
  14. package/dist/auto-type-generator/inline-object-traverser.d.ts.map +1 -0
  15. package/dist/auto-type-generator/inline-object-traverser.js +22 -0
  16. package/dist/auto-type-generator/inline-object-traverser.js.map +1 -0
  17. package/dist/auto-type-generator/inline-union-collector.d.ts +29 -0
  18. package/dist/auto-type-generator/inline-union-collector.d.ts.map +1 -0
  19. package/dist/auto-type-generator/inline-union-collector.js +216 -0
  20. package/dist/auto-type-generator/inline-union-collector.js.map +1 -0
  21. package/dist/auto-type-generator/inline-union-types.d.ts +29 -0
  22. package/dist/auto-type-generator/inline-union-types.d.ts.map +1 -0
  23. package/dist/auto-type-generator/inline-union-types.js +2 -0
  24. package/dist/auto-type-generator/inline-union-types.js.map +1 -0
  25. package/dist/auto-type-generator/inline-union-validator.d.ts +76 -0
  26. package/dist/auto-type-generator/inline-union-validator.d.ts.map +1 -0
  27. package/dist/auto-type-generator/inline-union-validator.js +329 -0
  28. package/dist/auto-type-generator/inline-union-validator.js.map +1 -0
  29. package/dist/auto-type-generator/naming-convention.d.ts +18 -1
  30. package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
  31. package/dist/auto-type-generator/naming-convention.js +16 -0
  32. package/dist/auto-type-generator/naming-convention.js.map +1 -1
  33. package/dist/auto-type-generator/resolve-type-generator.d.ts +20 -0
  34. package/dist/auto-type-generator/resolve-type-generator.d.ts.map +1 -0
  35. package/dist/auto-type-generator/resolve-type-generator.js +2 -0
  36. package/dist/auto-type-generator/resolve-type-generator.js.map +1 -0
  37. package/dist/auto-type-generator/resolver-field-iterator.d.ts +13 -0
  38. package/dist/auto-type-generator/resolver-field-iterator.d.ts.map +1 -0
  39. package/dist/auto-type-generator/resolver-field-iterator.js +22 -0
  40. package/dist/auto-type-generator/resolver-field-iterator.js.map +1 -0
  41. package/dist/auto-type-generator/typename-extractor.d.ts +26 -0
  42. package/dist/auto-type-generator/typename-extractor.d.ts.map +1 -0
  43. package/dist/auto-type-generator/typename-extractor.js +142 -0
  44. package/dist/auto-type-generator/typename-extractor.js.map +1 -0
  45. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts +35 -0
  46. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts.map +1 -0
  47. package/dist/auto-type-generator/typename-resolve-type-generator.js +177 -0
  48. package/dist/auto-type-generator/typename-resolve-type-generator.js.map +1 -0
  49. package/dist/auto-type-generator/typename-types.d.ts +43 -0
  50. package/dist/auto-type-generator/typename-types.d.ts.map +1 -0
  51. package/dist/auto-type-generator/typename-types.js +37 -0
  52. package/dist/auto-type-generator/typename-types.js.map +1 -0
  53. package/dist/auto-type-generator/typename-validator.d.ts +37 -0
  54. package/dist/auto-type-generator/typename-validator.d.ts.map +1 -0
  55. package/dist/auto-type-generator/typename-validator.js +206 -0
  56. package/dist/auto-type-generator/typename-validator.js.map +1 -0
  57. package/dist/cli.js +2 -0
  58. package/dist/cli.js.map +1 -1
  59. package/dist/commands/docs.d.ts +51 -0
  60. package/dist/commands/docs.d.ts.map +1 -0
  61. package/dist/commands/docs.js +154 -0
  62. package/dist/commands/docs.js.map +1 -0
  63. package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
  64. package/dist/gen-orchestrator/orchestrator.js +13 -6
  65. package/dist/gen-orchestrator/orchestrator.js.map +1 -1
  66. package/dist/resolver-extractor/extract-resolvers.d.ts +19 -1
  67. package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -1
  68. package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +5 -0
  69. package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
  70. package/dist/resolver-extractor/extractor/define-api-extractor.js +14 -61
  71. package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
  72. package/dist/resolver-extractor/index.d.ts +0 -1
  73. package/dist/resolver-extractor/index.d.ts.map +1 -1
  74. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +1 -0
  75. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -1
  76. package/dist/resolver-extractor/validator/abstract-resolver-validator.js +9 -5
  77. package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -1
  78. package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
  79. package/dist/schema-generator/emitter/code-emitter.js +20 -0
  80. package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
  81. package/dist/schema-generator/generate-schema.d.ts +1 -0
  82. package/dist/schema-generator/generate-schema.d.ts.map +1 -1
  83. package/dist/schema-generator/generate-schema.js +72 -3
  84. package/dist/schema-generator/generate-schema.js.map +1 -1
  85. package/dist/schema-generator/integrator/result-integrator.d.ts +14 -2
  86. package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
  87. package/dist/schema-generator/integrator/result-integrator.js +54 -1
  88. package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
  89. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts +2 -0
  90. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts.map +1 -1
  91. package/dist/schema-generator/resolver-collector/resolver-collector.js +22 -0
  92. package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -1
  93. package/dist/shared/enum-prefix-detector.d.ts +63 -0
  94. package/dist/shared/enum-prefix-detector.d.ts.map +1 -0
  95. package/dist/shared/enum-prefix-detector.js +80 -0
  96. package/dist/shared/enum-prefix-detector.js.map +1 -0
  97. package/dist/shared/ignore-fields-detector.d.ts +26 -0
  98. package/dist/shared/ignore-fields-detector.d.ts.map +1 -0
  99. package/dist/shared/ignore-fields-detector.js +83 -0
  100. package/dist/shared/ignore-fields-detector.js.map +1 -0
  101. package/dist/shared/ignore-fields-validator.d.ts +29 -0
  102. package/dist/shared/ignore-fields-validator.d.ts.map +1 -0
  103. package/dist/shared/ignore-fields-validator.js +43 -0
  104. package/dist/shared/ignore-fields-validator.js.map +1 -0
  105. package/dist/shared/index.d.ts +2 -0
  106. package/dist/shared/index.d.ts.map +1 -1
  107. package/dist/shared/index.js.map +1 -1
  108. package/dist/shared/source-location.d.ts +5 -0
  109. package/dist/shared/source-location.d.ts.map +1 -1
  110. package/dist/shared/source-location.js +7 -0
  111. package/dist/shared/source-location.js.map +1 -1
  112. package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
  113. package/dist/type-extractor/converter/graphql-converter.js +21 -7
  114. package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
  115. package/dist/type-extractor/extractor/field-type-resolver.js +42 -3
  116. package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -1
  117. package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
  118. package/dist/type-extractor/extractor/type-extractor.js +88 -23
  119. package/dist/type-extractor/extractor/type-extractor.js.map +1 -1
  120. package/dist/type-extractor/types/diagnostics.d.ts +1 -1
  121. package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
  122. package/dist/type-extractor/types/ts-type-reference-factory.d.ts +10 -2
  123. package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -1
  124. package/dist/type-extractor/types/ts-type-reference-factory.js +8 -2
  125. package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -1
  126. package/dist/type-extractor/types/typescript.d.ts +4 -0
  127. package/dist/type-extractor/types/typescript.d.ts.map +1 -1
  128. package/docs/coding-agents.md +64 -0
  129. package/docs/configuration.md +6 -20
  130. package/docs/getting-started.md +15 -12
  131. package/docs/index.md +36 -22
  132. package/docs/integration/apollo.md +8 -40
  133. package/docs/integration/drizzle.md +6 -10
  134. package/docs/integration/prisma.md +196 -0
  135. package/docs/integration/yoga.md +8 -40
  136. package/docs/schema/abstract-resolvers.md +117 -0
  137. package/docs/schema/directives.md +5 -0
  138. package/docs/schema/documentation.md +5 -0
  139. package/docs/schema/enums.md +99 -0
  140. package/docs/schema/fields.md +64 -0
  141. package/docs/schema/index.md +21 -0
  142. package/docs/schema/inputs.md +115 -15
  143. package/docs/schema/interfaces.md +31 -1
  144. package/docs/schema/objects.md +40 -0
  145. package/docs/schema/queries-mutations.md +136 -22
  146. package/docs/schema/scalars.md +5 -0
  147. package/docs/schema/unions.md +208 -1
  148. package/docs/what-is-gqlkit.md +13 -8
  149. package/package.json +6 -4
  150. package/src/auto-type-generator/auto-type-generator.ts +946 -201
  151. package/src/auto-type-generator/index.ts +42 -0
  152. package/src/auto-type-generator/inline-enum-collector.ts +187 -139
  153. package/src/auto-type-generator/inline-object-traverser.ts +49 -0
  154. package/src/auto-type-generator/inline-union-collector.ts +402 -0
  155. package/src/auto-type-generator/inline-union-types.ts +33 -0
  156. package/src/auto-type-generator/inline-union-validator.ts +482 -0
  157. package/src/auto-type-generator/naming-convention.ts +38 -1
  158. package/src/auto-type-generator/resolve-type-generator.ts +21 -0
  159. package/src/auto-type-generator/resolver-field-iterator.ts +39 -0
  160. package/src/auto-type-generator/typename-extractor.ts +230 -0
  161. package/src/auto-type-generator/typename-resolve-type-generator.ts +281 -0
  162. package/src/auto-type-generator/typename-types.ts +66 -0
  163. package/src/auto-type-generator/typename-validator.ts +326 -0
  164. package/src/cli.ts +2 -0
  165. package/src/commands/docs.ts +211 -0
  166. package/src/gen-orchestrator/orchestrator.ts +20 -6
  167. package/src/resolver-extractor/extract-resolvers.ts +19 -0
  168. package/src/resolver-extractor/extractor/define-api-extractor.ts +23 -89
  169. package/src/resolver-extractor/index.ts +0 -6
  170. package/src/resolver-extractor/validator/abstract-resolver-validator.ts +16 -8
  171. package/src/schema-generator/emitter/code-emitter.ts +34 -0
  172. package/src/schema-generator/generate-schema.ts +99 -2
  173. package/src/schema-generator/integrator/result-integrator.ts +70 -1
  174. package/src/schema-generator/resolver-collector/resolver-collector.ts +34 -0
  175. package/src/shared/enum-prefix-detector.ts +99 -0
  176. package/src/shared/ignore-fields-detector.ts +109 -0
  177. package/src/shared/ignore-fields-validator.ts +66 -0
  178. package/src/shared/index.ts +2 -0
  179. package/src/shared/source-location.ts +11 -0
  180. package/src/type-extractor/converter/graphql-converter.ts +31 -7
  181. package/src/type-extractor/extractor/field-type-resolver.ts +48 -3
  182. package/src/type-extractor/extractor/type-extractor.ts +103 -26
  183. package/src/type-extractor/types/diagnostics.ts +12 -2
  184. package/src/type-extractor/types/ts-type-reference-factory.ts +18 -5
  185. package/src/type-extractor/types/typescript.ts +4 -0
  186. package/dist/resolver-extractor/validator/only-validator.d.ts +0 -61
  187. package/dist/resolver-extractor/validator/only-validator.d.ts.map +0 -1
  188. package/dist/resolver-extractor/validator/only-validator.js +0 -76
  189. package/dist/resolver-extractor/validator/only-validator.js.map +0 -1
  190. package/src/resolver-extractor/validator/only-validator.ts +0 -158
@@ -62,6 +62,11 @@ export interface DefineApiResolverInfo {
62
62
  readonly args: ReadonlyArray<ArgumentDefinition> | null;
63
63
  readonly returnType: TSTypeReference;
64
64
  readonly sourceFile: string;
65
+ readonly sourceLocation: {
66
+ readonly file: string;
67
+ readonly line: number;
68
+ readonly column: number;
69
+ };
65
70
  readonly exportedInputTypes: ReadonlyArray<ExportedInputType>;
66
71
  readonly description: string | null;
67
72
  readonly deprecated: DeprecationInfo | null;
@@ -277,48 +282,15 @@ function shouldUnwrapAsGqlField(type: ts.Type): boolean {
277
282
  return hasDirectiveMetadata(type);
278
283
  }
279
284
 
280
- /**
281
- * Checks if a type is structurally equivalent to Record<string, never>.
282
- * This is a special type that represents "no arguments".
283
- * Detection is based on type structure, not type names.
284
- */
285
- function isNoArgsType(type: ts.Type, checker: ts.TypeChecker): boolean {
286
- const apparentType = checker.getApparentType(type);
287
-
288
- // Check for string index signature with 'never' value type
289
- const indexInfos = checker.getIndexInfosOfType(apparentType);
290
- const hasNeverStringIndex = indexInfos.some((info) => {
291
- return (
292
- (info.keyType.flags & ts.TypeFlags.String) !== 0 &&
293
- (info.type.flags & ts.TypeFlags.Never) !== 0
294
- );
295
- });
296
-
297
- if (!hasNeverStringIndex) {
298
- return false;
299
- }
300
-
301
- // Check that there are no named properties (excluding metadata properties)
302
- const properties = apparentType
303
- .getProperties()
304
- .filter((p) => !p.getName().startsWith("$"));
305
-
306
- return properties.length === 0;
307
- }
308
-
309
285
  /**
310
286
  * Checks if a type has only index signatures with no named properties.
311
287
  * Types like `{ [key: string]: number }` return true.
312
- * Does NOT return true for NoArgs type.
288
+ * Returns false for NoArgs type (Record<string, never>).
313
289
  */
314
290
  function hasOnlyIndexSignatures(
315
291
  type: ts.Type,
316
292
  checker: ts.TypeChecker,
317
293
  ): boolean {
318
- if (isNoArgsType(type, checker)) {
319
- return false;
320
- }
321
-
322
294
  const targetType = checker.getApparentType(type);
323
295
 
324
296
  const indexInfos = checker.getIndexInfosOfType(targetType);
@@ -328,6 +300,17 @@ function hasOnlyIndexSignatures(
328
300
  return false;
329
301
  }
330
302
 
303
+ const hasNeverStringIndex = indexInfos.some((info) => {
304
+ return (
305
+ (info.keyType.flags & ts.TypeFlags.String) !== 0 &&
306
+ (info.type.flags & ts.TypeFlags.Never) !== 0
307
+ );
308
+ });
309
+
310
+ if (hasNeverStringIndex) {
311
+ return false;
312
+ }
313
+
331
314
  const properties = targetType
332
315
  .getProperties()
333
316
  .filter((p) => !p.getName().startsWith(" $"));
@@ -458,37 +441,6 @@ function validateArgsType(
458
441
  return diagnostics;
459
442
  }
460
443
 
461
- /**
462
- * Checks if the extracted args are empty and the original type was not NoArgs.
463
- * This indicates the type resolved to an empty object.
464
- * Does not emit warnings for types that only have index signatures (they get INDEX_SIGNATURE_ONLY error instead).
465
- */
466
- function checkEmptyArgsType(
467
- argsType: ts.Type,
468
- argsTypeNode: ts.TypeNode,
469
- args: ArgumentDefinition[] | null,
470
- checker: ts.TypeChecker,
471
- ): Diagnostic | null {
472
- if (isNoArgsType(argsType, checker)) {
473
- return null;
474
- }
475
-
476
- if (hasOnlyIndexSignatures(argsType, checker)) {
477
- return null;
478
- }
479
-
480
- if (args !== null && args.length === 0) {
481
- const typeName = getTypeNameForDiagnostic(argsType, checker);
482
- return {
483
- code: "EMPTY_TYPE_PROPERTIES",
484
- message: `Type '${typeName}' has no properties. Consider adding properties or using a different type.`,
485
- severity: "warning",
486
- location: getSourceLocationFromNode(argsTypeNode),
487
- };
488
- }
489
- return null;
490
- }
491
-
492
444
  interface ExtractTypeArgumentsFromCallParams {
493
445
  readonly node: ts.CallExpression;
494
446
  readonly inputContext: FieldTypeResolverContext;
@@ -538,18 +490,6 @@ function extractTypeArgumentsFromCall(
538
490
  ? null
539
491
  : extractArgsFromType(argsType, inputContext, argsTypeNode);
540
492
 
541
- if (!isNoArgs) {
542
- const emptyDiagnostic = checkEmptyArgsType(
543
- argsType,
544
- argsTypeNode,
545
- args,
546
- checker,
547
- );
548
- if (emptyDiagnostic) {
549
- diagnostics.push(emptyDiagnostic);
550
- }
551
- }
552
-
553
493
  const directives = extractDirectivesFromTypeNode(
554
494
  directiveTypeNode,
555
495
  checker,
@@ -594,18 +534,6 @@ function extractTypeArgumentsFromCall(
594
534
  ? null
595
535
  : extractArgsFromType(argsType, inputContext, argsTypeNode);
596
536
 
597
- if (!isNoArgs) {
598
- const emptyDiagnostic = checkEmptyArgsType(
599
- argsType,
600
- argsTypeNode,
601
- args,
602
- checker,
603
- );
604
- if (emptyDiagnostic) {
605
- diagnostics.push(emptyDiagnostic);
606
- }
607
- }
608
-
609
537
  const directives = extractDirectivesFromTypeNode(directiveTypeNode, checker);
610
538
 
611
539
  return {
@@ -784,6 +712,11 @@ export function extractDefineApiResolvers(
784
712
  diagnostics.push(...typeInfo.diagnostics);
785
713
 
786
714
  const tsdocInfo = extractTsDocInfo(node, checker);
715
+ const sourceLocation = getSourceLocationFromNode(declaration.name) ?? {
716
+ file: filePath,
717
+ line: 1,
718
+ column: 1,
719
+ };
787
720
 
788
721
  resolvers.push({
789
722
  fieldName,
@@ -793,6 +726,7 @@ export function extractDefineApiResolvers(
793
726
  args: typeInfo.args,
794
727
  returnType: typeInfo.returnType,
795
728
  sourceFile: filePath,
729
+ sourceLocation,
796
730
  exportedInputTypes,
797
731
  description: tsdocInfo.description,
798
732
  deprecated: tsdocInfo.deprecated,
@@ -11,9 +11,3 @@ export type {
11
11
  AbstractResolverInfo,
12
12
  AbstractResolverKind,
13
13
  } from "./extractor/define-api-extractor.js";
14
- export type {
15
- OnlyConstraintViolation,
16
- OnlyConstraintViolationCode,
17
- ValidateOnlyConstraintsOptions,
18
- ValidationContext,
19
- } from "./validator/only-validator.js";
@@ -16,6 +16,7 @@ import type { AbstractResolverInfo } from "../extractor/define-api-extractor.js"
16
16
  export interface ValidateAbstractResolversOptions {
17
17
  readonly abstractResolvers: ReadonlyArray<AbstractResolverInfo>;
18
18
  readonly baseTypes: ReadonlyArray<BaseType>;
19
+ readonly typenameAutoResolveTypeNames?: ReadonlySet<string>;
19
20
  }
20
21
 
21
22
  export interface ValidateAbstractResolversResult {
@@ -113,21 +114,26 @@ function createMissingResolverDiagnostic(
113
114
  };
114
115
  }
115
116
 
117
+ interface DetectMissingAbstractTypeResolversParams {
118
+ readonly resolvers: ReadonlyArray<AbstractResolverInfo>;
119
+ readonly typeMap: Map<string, BaseType>;
120
+ readonly typenameAutoResolveTypeNames: ReadonlySet<string>;
121
+ }
122
+
116
123
  /**
117
124
  * Detects abstract types (union/interface) that have no resolveType defined
118
125
  * and whose member/implementing types don't all have isTypeOf defined.
119
126
  *
120
- * @param resolvers - All abstract resolvers to check
121
- * @param typeMap - Map of type names to their definitions
127
+ * @param params - Parameters including resolvers, type map, and auto-generated typename resolveType names
122
128
  * @returns Array of warning diagnostics for missing resolvers
123
129
  */
124
130
  function detectMissingAbstractTypeResolvers(
125
- resolvers: ReadonlyArray<AbstractResolverInfo>,
126
- typeMap: Map<string, BaseType>,
131
+ params: DetectMissingAbstractTypeResolversParams,
127
132
  ): Diagnostic[] {
133
+ const { resolvers, typeMap, typenameAutoResolveTypeNames } = params;
128
134
  const diagnostics: Diagnostic[] = [];
129
135
 
130
- const resolveTypeSet = new Set<string>();
136
+ const resolveTypeSet = new Set<string>(typenameAutoResolveTypeNames);
131
137
  const isTypeOfSet = new Set<string>();
132
138
 
133
139
  for (const resolver of resolvers) {
@@ -241,10 +247,12 @@ export function validateAbstractResolvers(
241
247
  );
242
248
  diagnostics.push(...duplicateDiagnostics);
243
249
 
244
- const missingResolverWarnings = detectMissingAbstractTypeResolvers(
245
- options.abstractResolvers,
250
+ const missingResolverWarnings = detectMissingAbstractTypeResolvers({
251
+ resolvers: options.abstractResolvers,
246
252
  typeMap,
247
- );
253
+ typenameAutoResolveTypeNames:
254
+ options.typenameAutoResolveTypeNames ?? new Set(),
255
+ });
248
256
  diagnostics.push(...missingResolverWarnings);
249
257
 
250
258
  return { diagnostics };
@@ -1,4 +1,6 @@
1
1
  import path from "node:path";
2
+ import type { AutoGeneratedResolveType } from "../../auto-type-generator/resolve-type-generator.js";
3
+ import { TYPENAME_FIELD_NAMES } from "../../auto-type-generator/typename-types.js";
2
4
  import { toPosixPath } from "../../shared/index.js";
3
5
  import type { CollectedScalarType } from "../../type-extractor/collector/scalar-collector.js";
4
6
  import {
@@ -248,6 +250,30 @@ function buildTypeResolverEntry(
248
250
  return ` ${type.typeName}: {\n${entries.join("\n")}\n },`;
249
251
  }
250
252
 
253
+ function getResolveTypeExpression(
254
+ fieldPattern: AutoGeneratedResolveType["fieldPattern"],
255
+ ): string {
256
+ const { usedFieldNames } = fieldPattern;
257
+
258
+ if (usedFieldNames.size === 1) {
259
+ const fieldName = [...usedFieldNames][0]!;
260
+ return `obj.${fieldName}`;
261
+ }
262
+
263
+ const expressions = TYPENAME_FIELD_NAMES.filter((name) =>
264
+ usedFieldNames.has(name),
265
+ ).map((name) => `obj.${name}`);
266
+
267
+ return expressions.join(" ?? ");
268
+ }
269
+
270
+ function buildAutoResolveTypeEntry(
271
+ autoResolveType: AutoGeneratedResolveType,
272
+ ): string {
273
+ const expression = getResolveTypeExpression(autoResolveType.fieldPattern);
274
+ return ` ${autoResolveType.unionTypeName}: {\n __resolveType: (obj) => ${expression},\n },`;
275
+ }
276
+
251
277
  function buildTypeEntries(
252
278
  resolverInfo: ResolverInfo,
253
279
  customScalars: ReadonlyArray<CollectedScalarType>,
@@ -269,15 +295,21 @@ function buildTypeEntries(
269
295
  abstractResolversByType.set(abstractResolver.typeName, abstractResolver);
270
296
  }
271
297
 
298
+ const autoResolveTypeByUnion = new Map<string, AutoGeneratedResolveType>(
299
+ resolverInfo.autoGeneratedResolveTypes.map((r) => [r.unionTypeName, r]),
300
+ );
301
+
272
302
  const allTypeNames = new Set<string>([
273
303
  ...typesWithFields.keys(),
274
304
  ...abstractResolversByType.keys(),
305
+ ...autoResolveTypeByUnion.keys(),
275
306
  ]);
276
307
  const sortedTypeNames = [...allTypeNames].sort();
277
308
 
278
309
  for (const typeName of sortedTypeNames) {
279
310
  const typeWithFields = typesWithFields.get(typeName);
280
311
  const abstractResolver = abstractResolversByType.get(typeName) ?? null;
312
+ const autoResolveType = autoResolveTypeByUnion.get(typeName);
281
313
 
282
314
  if (typeWithFields !== undefined) {
283
315
  typeEntries.push(
@@ -285,6 +317,8 @@ function buildTypeEntries(
285
317
  );
286
318
  } else if (abstractResolver !== null) {
287
319
  typeEntries.push(buildAbstractOnlyTypeEntry(abstractResolver));
320
+ } else if (autoResolveType !== undefined) {
321
+ typeEntries.push(buildAutoResolveTypeEntry(autoResolveType));
288
322
  }
289
323
  }
290
324
 
@@ -1,6 +1,10 @@
1
1
  import {
2
+ collectTypenameExtractions,
3
+ collectTypenameResolveTypes,
2
4
  generateAutoTypes,
3
5
  validateNameCollisions,
6
+ validateSchemaTypenames,
7
+ validateTypenames,
4
8
  } from "../auto-type-generator/index.js";
5
9
  import type { ExtractResolversResult } from "../resolver-extractor/index.js";
6
10
  import type { DirectiveDefinitionInfo } from "../shared/directive-definition-extractor.js";
@@ -28,6 +32,7 @@ export interface GenerateSchemaInput {
28
32
  readonly directiveDefinitions: ReadonlyArray<DirectiveDefinitionInfo> | null;
29
33
  readonly enablePruning: boolean | null;
30
34
  readonly sourceRoot: string | null;
35
+ readonly knownTypeNames: ReadonlySet<string> | null;
31
36
  }
32
37
 
33
38
  export interface GenerateSchemaResult {
@@ -52,11 +57,13 @@ export function generateSchema(
52
57
  directiveDefinitions,
53
58
  enablePruning,
54
59
  sourceRoot,
60
+ knownTypeNames,
55
61
  } = input;
56
62
 
57
63
  const autoTypeResult = generateAutoTypes({
58
64
  extractedTypes,
59
65
  resolversResult,
66
+ knownTypeNames: knownTypeNames ?? new Set(),
60
67
  });
61
68
 
62
69
  const autoTypeErrors = autoTypeResult.diagnostics.filter(
@@ -120,13 +127,103 @@ export function generateSchema(
120
127
  },
121
128
  };
122
129
 
130
+ const typeMap = new Map(
131
+ autoTypeResult.updatedExtractedTypes.map((t) => [t.metadata.name, t]),
132
+ );
133
+
134
+ const manualResolveTypeNames = new Set(
135
+ autoTypeResult.updatedResolversResult.abstractTypeResolvers
136
+ .filter((r) => r.kind === "resolveType")
137
+ .map((r) => r.targetTypeName),
138
+ );
139
+
140
+ const typenameResolveTypesResult = collectTypenameResolveTypes({
141
+ extractedTypes: autoTypeResult.updatedExtractedTypes,
142
+ typeMap,
143
+ manualResolveTypeNames,
144
+ });
145
+
146
+ const typenameExtractions = collectTypenameExtractions({
147
+ extractedTypes: autoTypeResult.updatedExtractedTypes,
148
+ typeMap,
149
+ });
150
+
151
+ const typenameValidationDiagnostics: Diagnostic[] = [];
152
+
153
+ for (const extraction of typenameExtractions) {
154
+ const abstractType = typeMap.get(extraction.abstractTypeName);
155
+ if (!abstractType) {
156
+ continue;
157
+ }
158
+ const validationResult = validateTypenames({
159
+ extractionResult: extraction,
160
+ sourceLocation: abstractType.metadata.sourceLocation,
161
+ inlineObjectMembers: abstractType.inlineObjectMembers,
162
+ });
163
+ typenameValidationDiagnostics.push(...validationResult.diagnostics);
164
+ }
165
+
166
+ const schemaTypenameValidationResult = validateSchemaTypenames({
167
+ objectTypes: autoTypeResult.updatedExtractedTypes,
168
+ typeMap,
169
+ });
170
+ typenameValidationDiagnostics.push(
171
+ ...schemaTypenameValidationResult.diagnostics,
172
+ );
173
+
174
+ if (typenameValidationDiagnostics.length > 0) {
175
+ return {
176
+ typeDefsCode: "",
177
+ sdlContent: "",
178
+ resolversCode: "",
179
+ diagnostics: typenameValidationDiagnostics,
180
+ hasErrors: true,
181
+ prunedTypes: null,
182
+ };
183
+ }
184
+
185
+ const generatedInlineObjectTypes =
186
+ typenameResolveTypesResult.generatedInlineObjectTypes;
187
+
188
+ const updatedTypesForIntegration: ExtractTypesResult =
189
+ generatedInlineObjectTypes.length > 0
190
+ ? {
191
+ types: updatedTypesResult.types.map((type) => {
192
+ if (type.kind !== "Union") {
193
+ return type;
194
+ }
195
+ const inlineTypes = generatedInlineObjectTypes.filter(
196
+ (g) => g.abstractTypeName === type.name,
197
+ );
198
+ if (inlineTypes.length === 0) {
199
+ return type;
200
+ }
201
+ const newMembers = inlineTypes.map((g) => g.typeName);
202
+ return {
203
+ ...type,
204
+ unionMembers: [
205
+ ...(type.unionMembers ?? []),
206
+ ...newMembers,
207
+ ].sort(),
208
+ };
209
+ }),
210
+ diagnostics: updatedTypesResult.diagnostics,
211
+ }
212
+ : updatedTypesResult;
213
+
214
+ const allAutoGeneratedTypes = [
215
+ ...autoTypeResult.autoGeneratedTypes,
216
+ ...typenameResolveTypesResult.generatedObjectTypes,
217
+ ];
218
+
123
219
  const integratedResult = integrate(
124
- updatedTypesResult,
220
+ updatedTypesForIntegration,
125
221
  autoTypeResult.updatedResolversResult,
126
222
  customScalarNames,
127
223
  customScalars,
128
224
  directiveDefinitions,
129
- autoTypeResult.autoGeneratedTypes,
225
+ allAutoGeneratedTypes,
226
+ typenameResolveTypesResult.autoResolveTypes,
130
227
  );
131
228
 
132
229
  let documentNode = buildDocumentNode(
@@ -1,4 +1,9 @@
1
- import type { AutoGeneratedType } from "../../auto-type-generator/index.js";
1
+ import type {
2
+ AutoGeneratedType,
3
+ ResolveTypeFieldPattern,
4
+ } from "../../auto-type-generator/index.js";
5
+ import type { TypenameAutoResolveTypeInfo } from "../../auto-type-generator/typename-resolve-type-generator.js";
6
+ import { createFieldNameSet } from "../../auto-type-generator/typename-types.js";
2
7
  import type {
3
8
  AbstractResolverInfo,
4
9
  ExtractResolversResult,
@@ -121,6 +126,12 @@ export interface StringEnumMappingInfo {
121
126
  readonly members: ReadonlyArray<StringEnumMember>;
122
127
  }
123
128
 
129
+ export interface AutoGeneratedUnionInfo {
130
+ readonly name: string;
131
+ readonly fieldPattern: ResolveTypeFieldPattern;
132
+ readonly hasAutoResolveType: boolean;
133
+ }
134
+
124
135
  export interface IntegratedResult {
125
136
  readonly baseTypes: ReadonlyArray<BaseType>;
126
137
  readonly inputTypes: ReadonlyArray<InputType>;
@@ -133,6 +144,12 @@ export interface IntegratedResult {
133
144
  readonly directiveDefinitions: ReadonlyArray<DirectiveDefinitionInfo> | null;
134
145
  /** Abstract type resolvers (resolveType and isTypeOf) */
135
146
  readonly abstractTypeResolvers: ReadonlyArray<AbstractResolverInfo>;
147
+ /** @deprecated Use autoGeneratedUnions instead */
148
+ readonly autoGeneratedUnionNames: ReadonlyArray<string>;
149
+ /** Auto-generated Union types that need __resolveType with field pattern info */
150
+ readonly autoGeneratedUnions: ReadonlyArray<AutoGeneratedUnionInfo>;
151
+ /** Union/Interface types that have typename-based auto resolveType */
152
+ readonly typenameAutoResolveTypes: ReadonlyArray<TypenameAutoResolveTypeInfo>;
136
153
  /** Numeric enum information for resolver generation */
137
154
  readonly numericEnums: ReadonlyArray<NumericEnumInfo>;
138
155
  /** String enum mappings for resolver generation */
@@ -257,6 +274,7 @@ export function integrate(
257
274
  collectedScalars?: ReadonlyArray<CollectedScalarType> | null,
258
275
  directiveDefinitions?: ReadonlyArray<DirectiveDefinitionInfo> | null,
259
276
  autoGeneratedTypes?: ReadonlyArray<AutoGeneratedType> | null,
277
+ typenameAutoResolveTypes?: ReadonlyArray<TypenameAutoResolveTypeInfo>,
260
278
  ): IntegratedResult {
261
279
  const directiveTypeAliasNames = new Set(
262
280
  directiveDefinitions?.map((d) => d.typeAliasName) ?? [],
@@ -281,6 +299,8 @@ export function integrate(
281
299
 
282
300
  const baseTypes: BaseType[] = [];
283
301
  const inputTypes: InputType[] = [];
302
+ const autoGeneratedUnionNames: string[] = [];
303
+ const autoGeneratedUnions: AutoGeneratedUnionInfo[] = [];
284
304
 
285
305
  for (const autoType of autoGeneratedTypes ?? []) {
286
306
  if (autoType.kind === "Object") {
@@ -327,6 +347,44 @@ export function integrate(
327
347
  sourceFile: autoType.sourceLocation.file,
328
348
  directives: null,
329
349
  });
350
+ } else if (autoType.kind === "Union") {
351
+ baseTypes.push({
352
+ name: autoType.name,
353
+ kind: "Union",
354
+ fields: null,
355
+ unionMembers: autoType.unionMembers ?? [],
356
+ enumValues: null,
357
+ isNumericEnum: false,
358
+ needsStringEnumMapping: false,
359
+ implementedInterfaces: null,
360
+ description: autoType.description,
361
+ deprecated: null,
362
+ sourceFile: autoType.sourceLocation.file,
363
+ directives: null,
364
+ });
365
+ autoGeneratedUnionNames.push(autoType.name);
366
+ autoGeneratedUnions.push({
367
+ name: autoType.name,
368
+ fieldPattern: autoType.resolveTypeFieldPattern ?? {
369
+ usedFieldNames: createFieldNameSet(["__typename"]),
370
+ },
371
+ hasAutoResolveType: autoType.resolveTypeFieldPattern !== null,
372
+ });
373
+ } else if (autoType.kind === "OneOfInputObject") {
374
+ inputTypes.push({
375
+ name: autoType.name,
376
+ fields: autoType.fields!.map((f) => ({
377
+ name: f.name,
378
+ type: f.type,
379
+ description: f.description,
380
+ deprecated: f.deprecated,
381
+ directives: f.directives,
382
+ defaultValue: f.defaultValue,
383
+ })),
384
+ sourceFile: autoType.sourceLocation.file,
385
+ description: autoType.description,
386
+ isOneOf: true,
387
+ });
330
388
  } else {
331
389
  inputTypes.push({
332
390
  name: autoType.name,
@@ -481,9 +539,17 @@ export function integrate(
481
539
  });
482
540
  }
483
541
 
542
+ const typenameAutoResolveTypeNames = new Set([
543
+ ...(typenameAutoResolveTypes?.map((t) => t.abstractTypeName) ?? []),
544
+ ...autoGeneratedUnions
545
+ .filter((u) => u.hasAutoResolveType)
546
+ .map((u) => u.name),
547
+ ]);
548
+
484
549
  const abstractResolverValidation = validateAbstractResolvers({
485
550
  abstractResolvers: resolversResult.abstractTypeResolvers,
486
551
  baseTypes,
552
+ typenameAutoResolveTypeNames,
487
553
  });
488
554
  diagnostics.push(...abstractResolverValidation.diagnostics);
489
555
 
@@ -680,6 +746,9 @@ export function integrate(
680
746
  ? directiveDefinitions
681
747
  : null,
682
748
  abstractTypeResolvers: resolversResult.abstractTypeResolvers,
749
+ autoGeneratedUnionNames,
750
+ autoGeneratedUnions,
751
+ typenameAutoResolveTypes: typenameAutoResolveTypes ?? [],
683
752
  numericEnums,
684
753
  stringEnumMappings,
685
754
  hasQuery,
@@ -1,3 +1,4 @@
1
+ import type { AutoGeneratedResolveType } from "../../auto-type-generator/resolve-type-generator.js";
1
2
  import type { AbstractResolverInfo } from "../../resolver-extractor/extractor/define-api-extractor.js";
2
3
  import type {
3
4
  ExtensionField,
@@ -28,6 +29,7 @@ export interface ResolverInfo {
28
29
  readonly types: ReadonlyArray<TypeResolvers>;
29
30
  readonly sourceFiles: ReadonlyArray<string>;
30
31
  readonly abstractTypeResolvers: ReadonlyArray<AbstractTypeResolverInfo>;
32
+ readonly autoGeneratedResolveTypes: ReadonlyArray<AutoGeneratedResolveType>;
31
33
  }
32
34
 
33
35
  function getResolverValueName(typeName: string): string {
@@ -104,6 +106,37 @@ export function collectResolverInfo(
104
106
  sourceFilesSet.add(resolver.sourceFile);
105
107
  }
106
108
 
109
+ const manualResolveTypeNames = new Set<string>(
110
+ abstractTypeResolvers
111
+ .filter((r) => r.resolverKey === "__resolveType")
112
+ .map((r) => r.typeName),
113
+ );
114
+
115
+ const inlineUnionResolveTypes: AutoGeneratedResolveType[] =
116
+ integratedResult.autoGeneratedUnions
117
+ .filter(
118
+ (u) => u.hasAutoResolveType && !manualResolveTypeNames.has(u.name),
119
+ )
120
+ .map((u) => ({
121
+ unionTypeName: u.name,
122
+ fieldPattern: u.fieldPattern,
123
+ }));
124
+
125
+ const typenameResolveTypes: AutoGeneratedResolveType[] =
126
+ integratedResult.typenameAutoResolveTypes
127
+ .filter((t) => !manualResolveTypeNames.has(t.abstractTypeName))
128
+ .map((t) => ({
129
+ unionTypeName: t.abstractTypeName,
130
+ fieldPattern: {
131
+ usedFieldNames: t.resolveTypePattern.usedFieldNames,
132
+ },
133
+ }));
134
+
135
+ const autoGeneratedResolveTypes = [
136
+ ...inlineUnionResolveTypes,
137
+ ...typenameResolveTypes,
138
+ ].sort((a, b) => a.unionTypeName.localeCompare(b.unionTypeName));
139
+
107
140
  const types: TypeResolvers[] = [...typeMap.entries()]
108
141
  .map(([typeName, fields]) => ({
109
142
  typeName,
@@ -119,5 +152,6 @@ export function collectResolverInfo(
119
152
  types,
120
153
  sourceFiles,
121
154
  abstractTypeResolvers,
155
+ autoGeneratedResolveTypes,
122
156
  };
123
157
  }