@gqlkit-ts/cli 0.6.0 → 0.7.1

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 (194) hide show
  1. package/dist/auto-type-generator/auto-type-generator.d.ts +7 -0
  2. package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
  3. package/dist/auto-type-generator/auto-type-generator.js +375 -55
  4. package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
  5. package/dist/auto-type-generator/discriminator-field-validator.d.ts +26 -0
  6. package/dist/auto-type-generator/discriminator-field-validator.d.ts.map +1 -0
  7. package/dist/auto-type-generator/discriminator-field-validator.js +242 -0
  8. package/dist/auto-type-generator/discriminator-field-validator.js.map +1 -0
  9. package/dist/auto-type-generator/discriminator-naming.d.ts +11 -0
  10. package/dist/auto-type-generator/discriminator-naming.d.ts.map +1 -0
  11. package/dist/auto-type-generator/discriminator-naming.js +15 -0
  12. package/dist/auto-type-generator/discriminator-naming.js.map +1 -0
  13. package/dist/auto-type-generator/discriminator-resolve-type-generator.d.ts +44 -0
  14. package/dist/auto-type-generator/discriminator-resolve-type-generator.d.ts.map +1 -0
  15. package/dist/auto-type-generator/discriminator-resolve-type-generator.js +77 -0
  16. package/dist/auto-type-generator/discriminator-resolve-type-generator.js.map +1 -0
  17. package/dist/auto-type-generator/index.d.ts +3 -0
  18. package/dist/auto-type-generator/index.d.ts.map +1 -1
  19. package/dist/auto-type-generator/index.js +3 -0
  20. package/dist/auto-type-generator/index.js.map +1 -1
  21. package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -1
  22. package/dist/auto-type-generator/inline-enum-collector.js +14 -7
  23. package/dist/auto-type-generator/inline-enum-collector.js.map +1 -1
  24. package/dist/auto-type-generator/inline-object-converter.d.ts +12 -0
  25. package/dist/auto-type-generator/inline-object-converter.d.ts.map +1 -0
  26. package/dist/auto-type-generator/inline-object-converter.js +72 -0
  27. package/dist/auto-type-generator/inline-object-converter.js.map +1 -0
  28. package/dist/auto-type-generator/inline-object-traverser.d.ts +2 -1
  29. package/dist/auto-type-generator/inline-object-traverser.d.ts.map +1 -1
  30. package/dist/auto-type-generator/inline-object-traverser.js +22 -4
  31. package/dist/auto-type-generator/inline-object-traverser.js.map +1 -1
  32. package/dist/auto-type-generator/inline-union-collector.d.ts.map +1 -1
  33. package/dist/auto-type-generator/inline-union-collector.js +20 -6
  34. package/dist/auto-type-generator/inline-union-collector.js.map +1 -1
  35. package/dist/auto-type-generator/inline-union-types.d.ts +2 -0
  36. package/dist/auto-type-generator/inline-union-types.d.ts.map +1 -1
  37. package/dist/auto-type-generator/inline-union-validator.js +3 -3
  38. package/dist/auto-type-generator/inline-union-validator.js.map +1 -1
  39. package/dist/auto-type-generator/intersection-flattener.d.ts +44 -0
  40. package/dist/auto-type-generator/intersection-flattener.d.ts.map +1 -0
  41. package/dist/auto-type-generator/intersection-flattener.js +398 -0
  42. package/dist/auto-type-generator/intersection-flattener.js.map +1 -0
  43. package/dist/auto-type-generator/naming-convention.d.ts +21 -0
  44. package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
  45. package/dist/auto-type-generator/naming-convention.js +145 -1
  46. package/dist/auto-type-generator/naming-convention.js.map +1 -1
  47. package/dist/auto-type-generator/typename-extractor.d.ts +2 -0
  48. package/dist/auto-type-generator/typename-extractor.d.ts.map +1 -1
  49. package/dist/auto-type-generator/typename-extractor.js +11 -3
  50. package/dist/auto-type-generator/typename-extractor.js.map +1 -1
  51. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts +2 -0
  52. package/dist/auto-type-generator/typename-resolve-type-generator.d.ts.map +1 -1
  53. package/dist/auto-type-generator/typename-resolve-type-generator.js +12 -84
  54. package/dist/auto-type-generator/typename-resolve-type-generator.js.map +1 -1
  55. package/dist/auto-type-generator/typename-types.d.ts +4 -0
  56. package/dist/auto-type-generator/typename-types.d.ts.map +1 -1
  57. package/dist/auto-type-generator/typename-types.js +6 -0
  58. package/dist/auto-type-generator/typename-types.js.map +1 -1
  59. package/dist/auto-type-generator/typename-validator.d.ts.map +1 -1
  60. package/dist/auto-type-generator/typename-validator.js +4 -3
  61. package/dist/auto-type-generator/typename-validator.js.map +1 -1
  62. package/dist/commands/gen.d.ts.map +1 -1
  63. package/dist/commands/gen.js +2 -1
  64. package/dist/commands/gen.js.map +1 -1
  65. package/dist/config/types.d.ts +7 -0
  66. package/dist/config/types.d.ts.map +1 -1
  67. package/dist/config-loader/index.d.ts +1 -1
  68. package/dist/config-loader/index.d.ts.map +1 -1
  69. package/dist/config-loader/index.js.map +1 -1
  70. package/dist/config-loader/loader.d.ts +6 -0
  71. package/dist/config-loader/loader.d.ts.map +1 -1
  72. package/dist/config-loader/loader.js +1 -0
  73. package/dist/config-loader/loader.js.map +1 -1
  74. package/dist/config-loader/validator.d.ts.map +1 -1
  75. package/dist/config-loader/validator.js +84 -1
  76. package/dist/config-loader/validator.js.map +1 -1
  77. package/dist/gen-orchestrator/orchestrator.d.ts +2 -1
  78. package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
  79. package/dist/gen-orchestrator/orchestrator.js +15 -2
  80. package/dist/gen-orchestrator/orchestrator.js.map +1 -1
  81. package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
  82. package/dist/resolver-extractor/extractor/define-api-extractor.js +4 -0
  83. package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
  84. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +2 -0
  85. package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -1
  86. package/dist/resolver-extractor/validator/abstract-resolver-validator.js +16 -3
  87. package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -1
  88. package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
  89. package/dist/schema-generator/emitter/code-emitter.js +13 -1
  90. package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
  91. package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.d.ts +18 -0
  92. package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.d.ts.map +1 -0
  93. package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.js +89 -0
  94. package/dist/schema-generator/emitter/discriminator-resolve-type-emitter.js.map +1 -0
  95. package/dist/schema-generator/generate-schema.d.ts +2 -0
  96. package/dist/schema-generator/generate-schema.d.ts.map +1 -1
  97. package/dist/schema-generator/generate-schema.js +69 -10
  98. package/dist/schema-generator/generate-schema.js.map +1 -1
  99. package/dist/schema-generator/integrator/result-integrator.d.ts +4 -0
  100. package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
  101. package/dist/schema-generator/integrator/result-integrator.js +18 -2
  102. package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
  103. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts +2 -0
  104. package/dist/schema-generator/resolver-collector/resolver-collector.d.ts.map +1 -1
  105. package/dist/schema-generator/resolver-collector/resolver-collector.js +4 -0
  106. package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -1
  107. package/dist/shared/constants.d.ts.map +1 -1
  108. package/dist/shared/constants.js +14 -1
  109. package/dist/shared/constants.js.map +1 -1
  110. package/dist/shared/enum-prefix-detector.d.ts.map +1 -1
  111. package/dist/shared/enum-prefix-detector.js +78 -8
  112. package/dist/shared/enum-prefix-detector.js.map +1 -1
  113. package/dist/shared/inline-object-utils.js +1 -1
  114. package/dist/shared/inline-object-utils.js.map +1 -1
  115. package/dist/shared/type-converter.d.ts.map +1 -1
  116. package/dist/shared/type-converter.js +55 -0
  117. package/dist/shared/type-converter.js.map +1 -1
  118. package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
  119. package/dist/type-extractor/converter/graphql-converter.js +11 -1
  120. package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
  121. package/dist/type-extractor/extractor/field-type-resolver.d.ts +18 -0
  122. package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -1
  123. package/dist/type-extractor/extractor/field-type-resolver.js +218 -16
  124. package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -1
  125. package/dist/type-extractor/extractor/type-extractor.d.ts +1 -0
  126. package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
  127. package/dist/type-extractor/extractor/type-extractor.js +127 -14
  128. package/dist/type-extractor/extractor/type-extractor.js.map +1 -1
  129. package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -1
  130. package/dist/type-extractor/extractor/type-name-collector.js +19 -4
  131. package/dist/type-extractor/extractor/type-name-collector.js.map +1 -1
  132. package/dist/type-extractor/types/diagnostics.d.ts +1 -1
  133. package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
  134. package/dist/type-extractor/types/index.d.ts +1 -1
  135. package/dist/type-extractor/types/index.d.ts.map +1 -1
  136. package/dist/type-extractor/types/index.js +1 -1
  137. package/dist/type-extractor/types/index.js.map +1 -1
  138. package/dist/type-extractor/types/ts-type-reference-factory.d.ts +7 -1
  139. package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -1
  140. package/dist/type-extractor/types/ts-type-reference-factory.js +18 -3
  141. package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -1
  142. package/dist/type-extractor/types/typescript.d.ts +3 -1
  143. package/dist/type-extractor/types/typescript.d.ts.map +1 -1
  144. package/dist/type-extractor/validator/type-validator.d.ts.map +1 -1
  145. package/dist/type-extractor/validator/type-validator.js +6 -1
  146. package/dist/type-extractor/validator/type-validator.js.map +1 -1
  147. package/docs/configuration.md +19 -0
  148. package/docs/index.md +1 -0
  149. package/docs/integration/ai-sdk.md +189 -0
  150. package/docs/schema/unions.md +117 -0
  151. package/package.json +2 -2
  152. package/src/auto-type-generator/auto-type-generator.ts +576 -58
  153. package/src/auto-type-generator/discriminator-field-validator.ts +368 -0
  154. package/src/auto-type-generator/discriminator-naming.ts +24 -0
  155. package/src/auto-type-generator/discriminator-resolve-type-generator.ts +136 -0
  156. package/src/auto-type-generator/index.ts +17 -0
  157. package/src/auto-type-generator/inline-enum-collector.ts +19 -4
  158. package/src/auto-type-generator/inline-object-converter.ts +100 -0
  159. package/src/auto-type-generator/inline-object-traverser.ts +33 -7
  160. package/src/auto-type-generator/inline-union-collector.ts +26 -4
  161. package/src/auto-type-generator/inline-union-types.ts +2 -0
  162. package/src/auto-type-generator/inline-union-validator.ts +3 -3
  163. package/src/auto-type-generator/intersection-flattener.ts +554 -0
  164. package/src/auto-type-generator/naming-convention.ts +205 -1
  165. package/src/auto-type-generator/typename-extractor.ts +17 -3
  166. package/src/auto-type-generator/typename-resolve-type-generator.ts +19 -108
  167. package/src/auto-type-generator/typename-types.ts +7 -0
  168. package/src/auto-type-generator/typename-validator.ts +4 -3
  169. package/src/commands/gen.ts +9 -2
  170. package/src/config/types.ts +10 -0
  171. package/src/config-loader/index.ts +1 -0
  172. package/src/config-loader/loader.ts +11 -0
  173. package/src/config-loader/validator.ts +100 -1
  174. package/src/gen-orchestrator/orchestrator.ts +19 -2
  175. package/src/resolver-extractor/extractor/define-api-extractor.ts +4 -0
  176. package/src/resolver-extractor/validator/abstract-resolver-validator.ts +20 -6
  177. package/src/schema-generator/emitter/code-emitter.ts +26 -1
  178. package/src/schema-generator/emitter/discriminator-resolve-type-emitter.ts +125 -0
  179. package/src/schema-generator/generate-schema.ts +100 -13
  180. package/src/schema-generator/integrator/result-integrator.ts +25 -1
  181. package/src/schema-generator/resolver-collector/resolver-collector.ts +7 -0
  182. package/src/shared/constants.ts +15 -1
  183. package/src/shared/enum-prefix-detector.ts +96 -8
  184. package/src/shared/inline-object-utils.ts +1 -1
  185. package/src/shared/type-converter.ts +63 -0
  186. package/src/type-extractor/converter/graphql-converter.ts +17 -1
  187. package/src/type-extractor/extractor/field-type-resolver.ts +266 -16
  188. package/src/type-extractor/extractor/type-extractor.ts +148 -11
  189. package/src/type-extractor/extractor/type-name-collector.ts +19 -4
  190. package/src/type-extractor/types/diagnostics.ts +10 -1
  191. package/src/type-extractor/types/index.ts +2 -1
  192. package/src/type-extractor/types/ts-type-reference-factory.ts +24 -3
  193. package/src/type-extractor/types/typescript.ts +6 -2
  194. package/src/type-extractor/validator/type-validator.ts +6 -1
@@ -1,6 +1,7 @@
1
1
  import { dirname, relative, resolve } from "node:path";
2
2
  import type ts from "typescript";
3
3
  import type {
4
+ ResolvedDiscriminatorFieldsMap,
4
5
  ResolvedOutputConfig,
5
6
  ResolvedScalarMapping,
6
7
  } from "../config-loader/index.js";
@@ -61,6 +62,7 @@ export interface GenerationConfig {
61
62
  readonly configDir: string | null;
62
63
  readonly customScalars: ReadonlyArray<ResolvedScalarMapping> | null;
63
64
  readonly tsconfigPath: string | null;
65
+ readonly discriminatorFields: ResolvedDiscriminatorFieldsMap;
64
66
  }
65
67
 
66
68
  export interface GeneratedFile {
@@ -82,6 +84,7 @@ interface TypesResult {
82
84
  detectedScalars: ReadonlyArray<ScalarMetadataInfo>;
83
85
  collectedScalars: ReadonlyArray<CollectedScalarType>;
84
86
  scalarMappingTable: ScalarBaseTypeMappingTable | null;
87
+ discoveredTypeNames: ReadonlySet<string>;
85
88
  }
86
89
 
87
90
  interface ResolversResult {
@@ -251,6 +254,7 @@ function extractTypesCore(params: ExtractTypesCoreParams): TypesResult {
251
254
  detectedScalars: extractionResult.detectedScalars,
252
255
  collectedScalars,
253
256
  scalarMappingTable,
257
+ discoveredTypeNames: extractionResult.discoveredTypeNames,
254
258
  };
255
259
  }
256
260
 
@@ -632,6 +636,18 @@ function extractTypesStep(ctx: PipelineContext): PipelineContext {
632
636
  return { ...ctx, typesResult };
633
637
  }
634
638
 
639
+ function augmentKnownTypeNamesStep(ctx: PipelineContext): PipelineContext {
640
+ if (ctx.aborted || !ctx.typesResult || !ctx.knownTypeNames) return ctx;
641
+
642
+ if (ctx.typesResult.discoveredTypeNames.size === 0) return ctx;
643
+
644
+ const augmented = new Set(ctx.knownTypeNames);
645
+ for (const name of ctx.typesResult.discoveredTypeNames) {
646
+ augmented.add(name);
647
+ }
648
+ return { ...ctx, knownTypeNames: augmented };
649
+ }
650
+
635
651
  function extractResolversStep(ctx: PipelineContext): PipelineContext {
636
652
  if (
637
653
  ctx.aborted ||
@@ -723,8 +739,7 @@ function generateSchemaStep(ctx: PipelineContext): {
723
739
 
724
740
  const { customScalarNames } = ctx.scalarConfig;
725
741
  const allCustomScalarNames = [
726
- ...customScalarNames,
727
- ...ctx.typesResult.detectedScalarNames,
742
+ ...new Set([...customScalarNames, ...ctx.typesResult.detectedScalarNames]),
728
743
  ];
729
744
 
730
745
  const schemaResult = generateSchema({
@@ -742,6 +757,7 @@ function generateSchemaStep(ctx: PipelineContext): {
742
757
  sourceRoot: ctx.config.cwd,
743
758
  knownTypeNames: ctx.knownTypeNames,
744
759
  importExtension: ctx.config.output.importExtension,
760
+ discriminatorFields: ctx.config.discriminatorFields,
745
761
  });
746
762
 
747
763
  const newDiagnostics = [...ctx.diagnostics, ...schemaResult.diagnostics];
@@ -796,6 +812,7 @@ export async function executeGeneration(
796
812
  ctx = collectTypeNamesStep(ctx);
797
813
  ctx = prepareScalarConfigStep(ctx);
798
814
  ctx = extractTypesStep(ctx);
815
+ ctx = augmentKnownTypeNamesStep(ctx);
799
816
  ctx = extractResolversStep(ctx);
800
817
  ctx = extractDirectivesStep(ctx);
801
818
  ctx = validateExtractionStep(ctx);
@@ -620,6 +620,8 @@ export function extractDefineApiResolvers(
620
620
  sourceFiles,
621
621
  scalarMappingTable,
622
622
  scalarMappingContext: "input",
623
+ discoveredTypes: null,
624
+ diagnostics: [],
623
625
  };
624
626
  const outputContext: FieldTypeResolverContext = {
625
627
  checker,
@@ -630,6 +632,8 @@ export function extractDefineApiResolvers(
630
632
  sourceFiles,
631
633
  scalarMappingTable,
632
634
  scalarMappingContext: "output",
635
+ discoveredTypes: null,
636
+ diagnostics: [],
633
637
  };
634
638
 
635
639
  for (const filePath of files) {
@@ -17,6 +17,8 @@ export interface ValidateAbstractResolversOptions {
17
17
  readonly abstractResolvers: ReadonlyArray<AbstractResolverInfo>;
18
18
  readonly baseTypes: ReadonlyArray<BaseType>;
19
19
  readonly typenameAutoResolveTypeNames?: ReadonlySet<string>;
20
+ /** Mapping from TypeScript type alias names to auto-generated GraphQL union names */
21
+ readonly tsAliasToGraphQLNameMap: ReadonlyMap<string, string>;
20
22
  }
21
23
 
22
24
  export interface ValidateAbstractResolversResult {
@@ -207,7 +209,22 @@ export function validateAbstractResolvers(
207
209
  typeMap.set(baseType.name, baseType);
208
210
  }
209
211
 
210
- for (const resolver of options.abstractResolvers) {
212
+ // Remap resolver target type names using TS alias → GraphQL name mapping
213
+ // e.g., defineResolveType<ItemPart> where ItemPart is a TS alias
214
+ // maps to the auto-generated union name "ContainerItems"
215
+ // Only remap resolveType resolvers — isTypeOf targets object types, not unions
216
+ const resolvers = options.abstractResolvers.map((resolver) => {
217
+ if (resolver.kind !== "resolveType") return resolver;
218
+ const mappedName = options.tsAliasToGraphQLNameMap.get(
219
+ resolver.targetTypeName,
220
+ );
221
+ if (mappedName !== undefined) {
222
+ return { ...resolver, targetTypeName: mappedName };
223
+ }
224
+ return resolver;
225
+ });
226
+
227
+ for (const resolver of resolvers) {
211
228
  const targetType = typeMap.get(resolver.targetTypeName);
212
229
 
213
230
  if (!targetType) {
@@ -241,14 +258,11 @@ export function validateAbstractResolvers(
241
258
  }
242
259
  }
243
260
 
244
- const duplicateDiagnostics = detectDuplicateResolvers(
245
- options.abstractResolvers,
246
- typeMap,
247
- );
261
+ const duplicateDiagnostics = detectDuplicateResolvers(resolvers, typeMap);
248
262
  diagnostics.push(...duplicateDiagnostics);
249
263
 
250
264
  const missingResolverWarnings = detectMissingAbstractTypeResolvers({
251
- resolvers: options.abstractResolvers,
265
+ resolvers,
252
266
  typeMap,
253
267
  typenameAutoResolveTypeNames:
254
268
  options.typenameAutoResolveTypeNames ?? new Set(),
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import type { DiscriminatorResolveTypeInfo } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
2
3
  import type { AutoGeneratedResolveType } from "../../auto-type-generator/resolve-type-generator.js";
3
4
  import { TYPENAME_FIELD_NAMES } from "../../auto-type-generator/typename-types.js";
4
5
  import type { ImportExtension } from "../../config/types.js";
@@ -19,6 +20,7 @@ import type {
19
20
  ResolverInfo,
20
21
  TypeResolvers,
21
22
  } from "../resolver-collector/resolver-collector.js";
23
+ import { buildDiscriminatorResolveTypeEntry } from "./discriminator-resolve-type-emitter.js";
22
24
 
23
25
  const GENERATED_FILE_HEADER =
24
26
  "// This file is auto-generated by gqlkit. DO NOT EDIT.";
@@ -374,11 +376,22 @@ function getResolveTypeExpression(
374
376
  return expressions.join(" ?? ");
375
377
  }
376
378
 
379
+ function buildResolveTypeObjType(
380
+ fieldPattern: AutoGeneratedResolveType["fieldPattern"],
381
+ ): string {
382
+ const fields = TYPENAME_FIELD_NAMES.filter((name) =>
383
+ fieldPattern.usedFieldNames.has(name),
384
+ ).map((name) => `${name}: string`);
385
+
386
+ return `{ ${fields.join("; ")} }`;
387
+ }
388
+
377
389
  function buildAutoResolveTypeEntry(
378
390
  autoResolveType: AutoGeneratedResolveType,
379
391
  ): string {
380
392
  const expression = getResolveTypeExpression(autoResolveType.fieldPattern);
381
- return ` ${autoResolveType.unionTypeName}: {\n __resolveType: (obj) => ${expression},\n },`;
393
+ const objType = buildResolveTypeObjType(autoResolveType.fieldPattern);
394
+ return ` ${autoResolveType.unionTypeName}: {\n __resolveType: (obj: ${objType}) => ${expression},\n },`;
382
395
  }
383
396
 
384
397
  function buildTypeEntries(
@@ -406,10 +419,16 @@ function buildTypeEntries(
406
419
  resolverInfo.autoGeneratedResolveTypes.map((r) => [r.unionTypeName, r]),
407
420
  );
408
421
 
422
+ const discriminatorResolveTypeByUnion = new Map<
423
+ string,
424
+ DiscriminatorResolveTypeInfo
425
+ >(resolverInfo.discriminatorResolveTypes.map((r) => [r.unionTypeName, r]));
426
+
409
427
  const allTypeNames = new Set<string>([
410
428
  ...typesWithFields.keys(),
411
429
  ...abstractResolversByType.keys(),
412
430
  ...autoResolveTypeByUnion.keys(),
431
+ ...discriminatorResolveTypeByUnion.keys(),
413
432
  ]);
414
433
  const sortedTypeNames = [...allTypeNames].sort();
415
434
 
@@ -417,6 +436,8 @@ function buildTypeEntries(
417
436
  const typeWithFields = typesWithFields.get(typeName);
418
437
  const abstractResolver = abstractResolversByType.get(typeName) ?? null;
419
438
  const autoResolveType = autoResolveTypeByUnion.get(typeName);
439
+ const discriminatorResolveType =
440
+ discriminatorResolveTypeByUnion.get(typeName);
420
441
 
421
442
  if (typeWithFields !== undefined) {
422
443
  typeEntries.push(
@@ -424,6 +445,10 @@ function buildTypeEntries(
424
445
  );
425
446
  } else if (abstractResolver !== null) {
426
447
  typeEntries.push(buildAbstractOnlyTypeEntry(abstractResolver));
448
+ } else if (discriminatorResolveType !== undefined) {
449
+ typeEntries.push(
450
+ buildDiscriminatorResolveTypeEntry(discriminatorResolveType),
451
+ );
427
452
  } else if (autoResolveType !== undefined) {
428
453
  typeEntries.push(buildAutoResolveTypeEntry(autoResolveType));
429
454
  }
@@ -0,0 +1,125 @@
1
+ import type {
2
+ DiscriminatorResolveTypeInfo,
3
+ DiscriminatorValueMapping,
4
+ } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
5
+
6
+ /**
7
+ * Groups value mappings by their value at a given field index.
8
+ * Null values at the field index mean the member doesn't have that field,
9
+ * so the member should be resolved directly at the current switch level.
10
+ */
11
+ function groupMappingsByFieldValue(
12
+ mappings: ReadonlyArray<DiscriminatorValueMapping>,
13
+ fieldIndex: number,
14
+ ): Map<string | null, DiscriminatorValueMapping[]> {
15
+ const groups = new Map<string | null, DiscriminatorValueMapping[]>();
16
+ for (const mapping of mappings) {
17
+ const value = mapping.values[fieldIndex] ?? null;
18
+ const group = groups.get(value) ?? [];
19
+ group.push(mapping);
20
+ groups.set(value, group);
21
+ }
22
+ return groups;
23
+ }
24
+
25
+ /**
26
+ * Builds a switch statement body for a given field level, recursing for nested fields.
27
+ * Returns an array of code lines (without leading indentation for the switch itself).
28
+ */
29
+ function buildSwitchBody(
30
+ mappings: ReadonlyArray<DiscriminatorValueMapping>,
31
+ fieldNames: ReadonlyArray<string>,
32
+ fieldIndex: number,
33
+ indent: string,
34
+ ): string[] {
35
+ const fieldName = fieldNames[fieldIndex]!;
36
+ const groups = groupMappingsByFieldValue(mappings, fieldIndex);
37
+ const lines: string[] = [];
38
+
39
+ lines.push(`${indent}switch (obj.${fieldName}) {`);
40
+
41
+ for (const [value, groupMappings] of groups) {
42
+ // Null value means the member doesn't have this field — resolved at the parent level
43
+ if (value === null) {
44
+ for (const mapping of groupMappings) {
45
+ lines.push(
46
+ `${indent} case "${mapping.values[fieldIndex - 1]!}": return "${mapping.memberGraphQLTypeName}";`,
47
+ );
48
+ }
49
+ continue;
50
+ }
51
+
52
+ const nextFieldIndex = fieldIndex + 1;
53
+ const hasMoreFields = nextFieldIndex < fieldNames.length;
54
+
55
+ // Check if all mappings in this group can be resolved directly
56
+ // (either no more fields, or only one mapping, or all remaining values are null)
57
+ const canResolveDirectly =
58
+ groupMappings.length === 1 &&
59
+ (!hasMoreFields || groupMappings[0]!.values[nextFieldIndex] === null);
60
+
61
+ if (!hasMoreFields || canResolveDirectly) {
62
+ // Single mapping or no more fields: emit direct return
63
+ if (groupMappings.length === 1) {
64
+ lines.push(
65
+ `${indent} case "${value}": return "${groupMappings[0]!.memberGraphQLTypeName}";`,
66
+ );
67
+ } else {
68
+ // Multiple mappings at the leaf level — shouldn't happen with unique tuples
69
+ for (const mapping of groupMappings) {
70
+ lines.push(
71
+ `${indent} case "${value}": return "${mapping.memberGraphQLTypeName}";`,
72
+ );
73
+ }
74
+ }
75
+ } else {
76
+ // Need nested switch for remaining fields
77
+ lines.push(`${indent} case "${value}":`);
78
+ const nestedLines = buildSwitchBody(
79
+ groupMappings,
80
+ fieldNames,
81
+ nextFieldIndex,
82
+ `${indent} `,
83
+ );
84
+ lines.push(...nestedLines);
85
+ }
86
+ }
87
+
88
+ lines.push(`${indent} default: return undefined;`);
89
+ lines.push(`${indent}}`);
90
+
91
+ return lines;
92
+ }
93
+
94
+ function buildObjTypeAnnotation(fieldNames: ReadonlyArray<string>): string {
95
+ const fields = fieldNames.map((name) => `${name}: string`);
96
+ return `{ ${fields.join("; ")} }`;
97
+ }
98
+
99
+ /**
100
+ * Builds a resolver map entry for a discriminator-based __resolveType.
101
+ *
102
+ * For single discriminator field:
103
+ * TypeName: {
104
+ * __resolveType: (obj: { field: string }) => {
105
+ * switch (obj.field) {
106
+ * case "val": return "MemberType";
107
+ * default: return undefined;
108
+ * }
109
+ * },
110
+ * },
111
+ *
112
+ * For multiple discriminator fields, generates nested switch statements.
113
+ */
114
+ export function buildDiscriminatorResolveTypeEntry(
115
+ info: DiscriminatorResolveTypeInfo,
116
+ ): string {
117
+ const { unionTypeName, fieldNames, valueMappings } = info;
118
+ const objType = buildObjTypeAnnotation(fieldNames);
119
+ const baseIndent = " ";
120
+
121
+ const switchLines = buildSwitchBody(valueMappings, fieldNames, 0, baseIndent);
122
+ const switchBody = switchLines.join("\n");
123
+
124
+ return ` ${unionTypeName}: {\n __resolveType: (obj: ${objType}) => {\n${switchBody}\n },\n },`;
125
+ }
@@ -1,12 +1,16 @@
1
1
  import {
2
+ collectDiscriminatorResolveTypes,
2
3
  collectTypenameExtractions,
3
4
  collectTypenameResolveTypes,
5
+ flattenIntersectionMembers,
4
6
  generateAutoTypes,
7
+ validateDiscriminatorFields,
5
8
  validateNameCollisions,
6
9
  validateSchemaTypenames,
7
10
  validateTypenames,
8
11
  } from "../auto-type-generator/index.js";
9
12
  import type { ImportExtension } from "../config/types.js";
13
+ import type { ResolvedDiscriminatorFieldsMap } from "../config-loader/index.js";
10
14
  import type { ExtractResolversResult } from "../resolver-extractor/index.js";
11
15
  import type { DirectiveDefinitionInfo } from "../shared/directive-definition-extractor.js";
12
16
  import type { CollectedScalarType } from "../type-extractor/collector/scalar-collector.js";
@@ -35,6 +39,7 @@ export interface GenerateSchemaInput {
35
39
  readonly sourceRoot: string | null;
36
40
  readonly knownTypeNames: ReadonlySet<string> | null;
37
41
  readonly importExtension: ImportExtension;
42
+ readonly discriminatorFields: ResolvedDiscriminatorFieldsMap;
38
43
  }
39
44
 
40
45
  export interface GenerateSchemaResult {
@@ -67,6 +72,7 @@ export function generateSchema(
67
72
  extractedTypes,
68
73
  resolversResult,
69
74
  knownTypeNames: knownTypeNames ?? new Set(),
75
+ discriminatorFields: input.discriminatorFields,
70
76
  });
71
77
 
72
78
  const autoTypeErrors = autoTypeResult.diagnostics.filter(
@@ -105,11 +111,52 @@ export function generateSchema(
105
111
  };
106
112
  }
107
113
 
114
+ // Flatten intersection-expanded inline object members for discriminator unions.
115
+ // TypeScript distributes intersections over unions, creating duplicate members
116
+ // that must be merged before discriminator field validation.
117
+ const flattenedExtractedTypes = flattenIntersectionMembers({
118
+ extractedTypes: autoTypeResult.updatedExtractedTypes,
119
+ discriminatorFields: input.discriminatorFields,
120
+ });
121
+
108
122
  // Re-convert to GraphQL types using updated extracted types
109
123
  // This resolves __INLINE_OBJECT__ references to auto-generated type names
110
- const updatedConversionResult = convertToGraphQL(
111
- autoTypeResult.updatedExtractedTypes,
124
+ const updatedConversionResult = convertToGraphQL(flattenedExtractedTypes);
125
+ const typeMap = new Map(
126
+ flattenedExtractedTypes.map((t) => [t.metadata.name, t]),
112
127
  );
128
+
129
+ // Validate discriminator fields against extracted types
130
+ const inlineDiscriminatorUnionNames = new Set(
131
+ autoTypeResult.inlineDiscriminatorResolveTypes.map(
132
+ (rt) => rt.unionTypeName,
133
+ ),
134
+ );
135
+ const discriminatorValidationResult = validateDiscriminatorFields({
136
+ discriminatorFields: input.discriminatorFields,
137
+ extractedTypes: flattenedExtractedTypes,
138
+ typeMap,
139
+ inlineDiscriminatorUnionNames,
140
+ });
141
+ const discriminatorErrors = discriminatorValidationResult.diagnostics.filter(
142
+ (d) => d.severity === "error",
143
+ );
144
+ if (discriminatorErrors.length > 0) {
145
+ return {
146
+ typeDefsCode: "",
147
+ sdlContent: "",
148
+ resolversCode: "",
149
+ diagnostics: discriminatorValidationResult.diagnostics,
150
+ hasErrors: true,
151
+ prunedTypes: null,
152
+ };
153
+ }
154
+
155
+ const discriminatorWarnings =
156
+ discriminatorValidationResult.diagnostics.filter(
157
+ (d) => d.severity === "warning",
158
+ );
159
+
113
160
  const updatedTypesResult: ExtractTypesResult = {
114
161
  types: updatedConversionResult.types,
115
162
  diagnostics: {
@@ -126,29 +173,48 @@ export function generateSchema(
126
173
  (d) => d.severity === "warning",
127
174
  ),
128
175
  ...autoTypeResult.diagnostics.filter((d) => d.severity === "warning"),
176
+ ...discriminatorWarnings,
129
177
  ],
130
178
  },
131
179
  };
132
180
 
133
- const typeMap = new Map(
134
- autoTypeResult.updatedExtractedTypes.map((t) => [t.metadata.name, t]),
135
- );
136
-
137
181
  const manualResolveTypeNames = new Set(
138
182
  autoTypeResult.updatedResolversResult.abstractTypeResolvers
139
183
  .filter((r) => r.kind === "resolveType")
140
184
  .map((r) => r.targetTypeName),
141
185
  );
142
186
 
187
+ // Collect discriminator resolve types from validated entries
188
+ const discriminatorResolveTypesResult = collectDiscriminatorResolveTypes({
189
+ validatedEntries: discriminatorValidationResult.validatedEntries,
190
+ manualResolveTypeNames,
191
+ extractedTypes: flattenedExtractedTypes,
192
+ typeMap,
193
+ });
194
+
195
+ // Merge inline discriminator resolveTypes from auto-type generation
196
+ // (for inline unions flattened by discriminator fields, e.g. UIMessagePart<...>[])
197
+ const mergedDiscriminatorResolveTypes = [
198
+ ...discriminatorResolveTypesResult.discriminatorResolveTypes,
199
+ ...autoTypeResult.inlineDiscriminatorResolveTypes,
200
+ ];
201
+ // discriminatorResolveTypeNames are derived by integrate() from the merged list
202
+
203
+ const discriminatorFieldUnionNames = new Set(
204
+ input.discriminatorFields.keys(),
205
+ );
206
+
143
207
  const typenameResolveTypesResult = collectTypenameResolveTypes({
144
- extractedTypes: autoTypeResult.updatedExtractedTypes,
208
+ extractedTypes: flattenedExtractedTypes,
145
209
  typeMap,
146
210
  manualResolveTypeNames,
211
+ discriminatorFieldUnionNames,
147
212
  });
148
213
 
149
214
  const typenameExtractions = collectTypenameExtractions({
150
- extractedTypes: autoTypeResult.updatedExtractedTypes,
215
+ extractedTypes: flattenedExtractedTypes,
151
216
  typeMap,
217
+ discriminatorFieldUnionNames,
152
218
  });
153
219
 
154
220
  const typenameValidationDiagnostics: Diagnostic[] = [];
@@ -167,7 +233,7 @@ export function generateSchema(
167
233
  }
168
234
 
169
235
  const schemaTypenameValidationResult = validateSchemaTypenames({
170
- objectTypes: autoTypeResult.updatedExtractedTypes,
236
+ objectTypes: flattenedExtractedTypes,
171
237
  typeMap,
172
238
  });
173
239
  typenameValidationDiagnostics.push(
@@ -188,20 +254,38 @@ export function generateSchema(
188
254
  const generatedInlineObjectTypes =
189
255
  typenameResolveTypesResult.generatedInlineObjectTypes;
190
256
 
257
+ // Collect all generated object types that need to be added as union members
258
+ // (both typename-based and discriminator-based inline objects)
259
+ const discriminatorGeneratedObjectTypes =
260
+ discriminatorResolveTypesResult.generatedObjectTypes;
261
+
191
262
  const updatedTypesForIntegration: ExtractTypesResult =
192
- generatedInlineObjectTypes.length > 0
263
+ generatedInlineObjectTypes.length > 0 ||
264
+ discriminatorGeneratedObjectTypes.length > 0
193
265
  ? {
194
266
  types: updatedTypesResult.types.map((type) => {
195
267
  if (type.kind !== "Union") {
196
268
  return type;
197
269
  }
198
- const inlineTypes = generatedInlineObjectTypes.filter(
270
+ const typenameInlineTypes = generatedInlineObjectTypes.filter(
199
271
  (g) => g.abstractTypeName === type.name,
200
272
  );
201
- if (inlineTypes.length === 0) {
273
+ const discriminatorInlineTypes =
274
+ discriminatorGeneratedObjectTypes.filter(
275
+ (g) =>
276
+ g.generatedFrom !== null &&
277
+ g.generatedFrom.parentTypeName === type.name,
278
+ );
279
+ if (
280
+ typenameInlineTypes.length === 0 &&
281
+ discriminatorInlineTypes.length === 0
282
+ ) {
202
283
  return type;
203
284
  }
204
- const newMembers = inlineTypes.map((g) => g.typeName);
285
+ const newMembers = [
286
+ ...typenameInlineTypes.map((g) => g.typeName),
287
+ ...discriminatorInlineTypes.map((g) => g.name),
288
+ ];
205
289
  return {
206
290
  ...type,
207
291
  unionMembers: [
@@ -217,6 +301,7 @@ export function generateSchema(
217
301
  const allAutoGeneratedTypes = [
218
302
  ...autoTypeResult.autoGeneratedTypes,
219
303
  ...typenameResolveTypesResult.generatedObjectTypes,
304
+ ...discriminatorGeneratedObjectTypes,
220
305
  ];
221
306
 
222
307
  const integratedResult = integrate({
@@ -227,6 +312,8 @@ export function generateSchema(
227
312
  directiveDefinitions,
228
313
  autoGeneratedTypes: allAutoGeneratedTypes,
229
314
  typenameAutoResolveTypes: typenameResolveTypesResult.autoResolveTypes,
315
+ discriminatorResolveTypes: mergedDiscriminatorResolveTypes,
316
+ tsAliasToGraphQLNameMap: autoTypeResult.tsAliasToGraphQLNameMap,
230
317
  });
231
318
 
232
319
  let documentNode = buildDocumentNode(integratedResult, { sourceRoot });
@@ -1,3 +1,4 @@
1
+ import type { DiscriminatorResolveTypeInfo } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
1
2
  import type {
2
3
  AutoGeneratedType,
3
4
  ResolveTypeFieldPattern,
@@ -141,6 +142,7 @@ export interface IntegratedResult {
141
142
  readonly abstractTypeResolvers: ReadonlyArray<AbstractResolverInfo>;
142
143
  readonly autoGeneratedUnions: ReadonlyArray<AutoGeneratedUnionInfo>;
143
144
  readonly typenameAutoResolveTypes: ReadonlyArray<TypenameAutoResolveTypeInfo>;
145
+ readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo>;
144
146
  readonly numericEnums: ReadonlyArray<NumericEnumInfo>;
145
147
  readonly stringEnumMappings: ReadonlyArray<StringEnumMappingInfo>;
146
148
  readonly hasQuery: boolean;
@@ -276,6 +278,8 @@ export interface IntegrateParams {
276
278
  readonly directiveDefinitions: ReadonlyArray<DirectiveDefinitionInfo> | null;
277
279
  readonly autoGeneratedTypes: ReadonlyArray<AutoGeneratedType> | null;
278
280
  readonly typenameAutoResolveTypes: ReadonlyArray<TypenameAutoResolveTypeInfo> | null;
281
+ readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo> | null;
282
+ readonly tsAliasToGraphQLNameMap: ReadonlyMap<string, string> | null;
279
283
  }
280
284
 
281
285
  export function integrate(params: IntegrateParams): IntegratedResult {
@@ -287,6 +291,8 @@ export function integrate(params: IntegrateParams): IntegratedResult {
287
291
  directiveDefinitions,
288
292
  autoGeneratedTypes,
289
293
  typenameAutoResolveTypes,
294
+ discriminatorResolveTypes,
295
+ tsAliasToGraphQLNameMap,
290
296
  } = params;
291
297
  const directiveTypeAliasNames = new Set(
292
298
  directiveDefinitions?.map((d) => d.typeAliasName) ?? [],
@@ -527,15 +533,32 @@ export function integrate(params: IntegrateParams): IntegratedResult {
527
533
  ...autoGeneratedUnions
528
534
  .filter((u) => u.hasAutoResolveType)
529
535
  .map((u) => u.name),
536
+ ...(discriminatorResolveTypes?.map((t) => t.unionTypeName) ?? []),
530
537
  ]);
531
538
 
539
+ const aliasMap = tsAliasToGraphQLNameMap ?? new Map<string, string>();
540
+
532
541
  const abstractResolverValidation = validateAbstractResolvers({
533
542
  abstractResolvers: resolversResult.abstractTypeResolvers,
534
543
  baseTypes,
535
544
  typenameAutoResolveTypeNames,
545
+ tsAliasToGraphQLNameMap: aliasMap,
536
546
  });
537
547
  diagnostics.push(...abstractResolverValidation.diagnostics);
538
548
 
549
+ // Remap abstract resolver target type names from TS aliases to GraphQL names
550
+ // Only remap resolveType resolvers — isTypeOf targets object types, not unions
551
+ const remappedAbstractResolvers =
552
+ aliasMap.size > 0
553
+ ? resolversResult.abstractTypeResolvers.map((resolver) => {
554
+ if (resolver.kind !== "resolveType") return resolver;
555
+ const mappedName = aliasMap.get(resolver.targetTypeName);
556
+ return mappedName !== undefined
557
+ ? { ...resolver, targetTypeName: mappedName }
558
+ : resolver;
559
+ })
560
+ : resolversResult.abstractTypeResolvers;
561
+
539
562
  const knownTypeNames = new Set([
540
563
  ...baseTypes.map((t) => t.name),
541
564
  ...inputTypes.map((t) => t.name),
@@ -733,9 +756,10 @@ export function integrate(params: IntegrateParams): IntegratedResult {
733
756
  directiveDefinitions && directiveDefinitions.length > 0
734
757
  ? directiveDefinitions
735
758
  : null,
736
- abstractTypeResolvers: resolversResult.abstractTypeResolvers,
759
+ abstractTypeResolvers: remappedAbstractResolvers,
737
760
  autoGeneratedUnions,
738
761
  typenameAutoResolveTypes: typenameAutoResolveTypes ?? [],
762
+ discriminatorResolveTypes: discriminatorResolveTypes ?? [],
739
763
  numericEnums,
740
764
  stringEnumMappings,
741
765
  hasQuery,
@@ -1,3 +1,4 @@
1
+ import type { DiscriminatorResolveTypeInfo } from "../../auto-type-generator/discriminator-resolve-type-generator.js";
1
2
  import type { AutoGeneratedResolveType } from "../../auto-type-generator/resolve-type-generator.js";
2
3
  import type { AbstractResolverInfo } from "../../resolver-extractor/extractor/define-api-extractor.js";
3
4
  import type {
@@ -30,6 +31,7 @@ export interface ResolverInfo {
30
31
  readonly sourceFiles: ReadonlyArray<string>;
31
32
  readonly abstractTypeResolvers: ReadonlyArray<AbstractTypeResolverInfo>;
32
33
  readonly autoGeneratedResolveTypes: ReadonlyArray<AutoGeneratedResolveType>;
34
+ readonly discriminatorResolveTypes: ReadonlyArray<DiscriminatorResolveTypeInfo>;
33
35
  }
34
36
 
35
37
  function getResolverValueName(typeName: string): string {
@@ -148,10 +150,15 @@ export function collectResolverInfo(
148
150
 
149
151
  const sourceFiles = [...sourceFilesSet].sort((a, b) => a.localeCompare(b));
150
152
 
153
+ const discriminatorResolveTypes = [
154
+ ...integratedResult.discriminatorResolveTypes,
155
+ ].sort((a, b) => a.unionTypeName.localeCompare(b.unionTypeName));
156
+
151
157
  return {
152
158
  types,
153
159
  sourceFiles,
154
160
  abstractTypeResolvers,
155
161
  autoGeneratedResolveTypes,
162
+ discriminatorResolveTypes,
156
163
  };
157
164
  }
@@ -39,6 +39,19 @@ const TS_ANONYMOUS_TYPE_SYMBOL = "__type";
39
39
  */
40
40
  const TS_ARRAY_TYPE_SYMBOL = "Array";
41
41
 
42
+ /**
43
+ * Object literal type symbol name.
44
+ *
45
+ * TypeScript uses `__object` as the internal symbol name for object literal
46
+ * expressions (value-space). This differs from `__type` which is used for
47
+ * anonymous object types in type-space.
48
+ *
49
+ * This commonly occurs when a type is derived via `typeof` from a variable
50
+ * initialized with an object literal, especially when the value comes from
51
+ * an external library.
52
+ */
53
+ const TS_OBJECT_LITERAL_TYPE_SYMBOL = "__object";
54
+
42
55
  /**
43
56
  * Checks if a symbol name represents a TypeScript internal/anonymous type
44
57
  * that should not be used directly as a GraphQL type name.
@@ -49,7 +62,8 @@ const TS_ARRAY_TYPE_SYMBOL = "Array";
49
62
  export function isInternalTypeSymbol(symbolName: string): boolean {
50
63
  return (
51
64
  symbolName === TS_ANONYMOUS_TYPE_SYMBOL ||
52
- symbolName === TS_ARRAY_TYPE_SYMBOL
65
+ symbolName === TS_ARRAY_TYPE_SYMBOL ||
66
+ symbolName === TS_OBJECT_LITERAL_TYPE_SYMBOL
53
67
  );
54
68
  }
55
69