@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
@@ -7,6 +7,11 @@ import type {
7
7
  DirectiveArgumentValue,
8
8
  DirectiveInfo,
9
9
  } from "../shared/directive-detector.js";
10
+ import {
11
+ detectEnumPrefix,
12
+ stripEnumPrefix,
13
+ } from "../shared/enum-prefix-detector.js";
14
+ import { getSourceLocationOrDefault } from "../shared/source-location.js";
10
15
  import type { DeprecationInfo } from "../shared/tsdoc-parser.js";
11
16
  import { convertTsTypeToGraphQLType } from "../shared/type-converter.js";
12
17
  import {
@@ -24,15 +29,38 @@ import {
24
29
  type SourceLocation,
25
30
  } from "../type-extractor/types/index.js";
26
31
  import {
32
+ collectInlineEnumsFromPayloads,
27
33
  collectInlineEnumsFromResolvers,
28
34
  collectInlineEnumsFromTypes,
29
35
  type InlineEnumWithContext,
30
36
  } from "./inline-enum-collector.js";
37
+ import {
38
+ collectInlineUnionsFromPayloads,
39
+ collectInlineUnionsFromResolvers,
40
+ collectInlineUnionsFromTypes,
41
+ type InlineUnionWithContext,
42
+ } from "./inline-union-collector.js";
43
+ import type { InlineUnionMemberInfo } from "./inline-union-types.js";
44
+ import {
45
+ type ValidatedTypenameInfo,
46
+ validateOneOfMembers,
47
+ validateUnionMembers,
48
+ validateUnionMemberTypenames,
49
+ } from "./inline-union-validator.js";
31
50
  import {
32
51
  type AutoTypeNameContext,
33
52
  buildFieldContext,
34
53
  generateAutoTypeName,
54
+ isInputTypeName,
35
55
  } from "./naming-convention.js";
56
+ import type { ResolveTypeFieldPattern } from "./resolve-type-generator.js";
57
+ import { forEachResolverField } from "./resolver-field-iterator.js";
58
+ import {
59
+ createFieldNameSet,
60
+ findTypenameProperty,
61
+ type TypenameFieldInfo,
62
+ type TypenameFieldName,
63
+ } from "./typename-types.js";
36
64
 
37
65
  /**
38
66
  * Information about where an auto-generated type was generated from.
@@ -40,7 +68,11 @@ import {
40
68
  export interface GeneratedFromInfo {
41
69
  readonly parentTypeName: string | null;
42
70
  readonly fieldPath: ReadonlyArray<string>;
43
- readonly context: "typeField" | "inputField" | "resolverArg";
71
+ readonly context:
72
+ | "typeField"
73
+ | "inputField"
74
+ | "resolverArg"
75
+ | "resolverPayload";
44
76
  }
45
77
 
46
78
  /**
@@ -72,21 +104,31 @@ export interface AutoGeneratedEnumValue {
72
104
  */
73
105
  export interface AutoGeneratedType {
74
106
  readonly name: string;
75
- readonly kind: "Object" | "InputObject" | "Enum";
76
- /** Fields for Object/InputObject kinds (null for Enum) */
107
+ readonly kind:
108
+ | "Object"
109
+ | "InputObject"
110
+ | "Enum"
111
+ | "Union"
112
+ | "OneOfInputObject";
113
+ /** Fields for Object/InputObject/OneOfInputObject kinds (null for Enum/Union) */
77
114
  readonly fields: ReadonlyArray<AutoGeneratedField> | null;
78
- /** Enum values for Enum kind (null for Object/InputObject) */
115
+ /** Enum values for Enum kind (null for Object/InputObject/Union/OneOfInputObject) */
79
116
  readonly enumValues: ReadonlyArray<AutoGeneratedEnumValue> | null;
117
+ /** Union members for Union kind (null for Object/InputObject/Enum/OneOfInputObject) */
118
+ readonly unionMembers: ReadonlyArray<string> | null;
80
119
  /** True if enum needs value mapping (at least one value converted) */
81
120
  readonly needsStringEnumMapping: boolean;
82
121
  readonly sourceLocation: SourceLocation;
83
122
  readonly generatedFrom: GeneratedFromInfo;
84
123
  readonly description: string | null;
124
+ /** For Union types from resolverPayload context: the field pattern for resolveType */
125
+ readonly resolveTypeFieldPattern: ResolveTypeFieldPattern | null;
85
126
  }
86
127
 
87
128
  export interface AutoTypeGeneratorInput {
88
129
  readonly extractedTypes: ReadonlyArray<ExtractedTypeInfo>;
89
130
  readonly resolversResult: ExtractResolversResult;
131
+ readonly knownTypeNames: ReadonlySet<string>;
90
132
  }
91
133
 
92
134
  export interface AutoTypeGeneratorResult {
@@ -101,10 +143,70 @@ interface InlineObjectWithContext {
101
143
  readonly context: AutoTypeNameContext;
102
144
  readonly sourceLocation: SourceLocation;
103
145
  readonly nullable: boolean;
146
+ /** TSDoc description from the inline object type alias (Requirement 7.2) */
147
+ readonly description: string | null;
148
+ /** @deprecated tag from the inline object type alias (Requirement 7.3) */
149
+ readonly deprecated: DeprecationInfo | null;
150
+ }
151
+
152
+ type ContextBuilderFn = (
153
+ nestedPath: ReadonlyArray<string>,
154
+ ) => AutoTypeNameContext;
155
+
156
+ interface ExtractNestedInlineObjectsParams {
157
+ readonly properties: ReadonlyArray<InlineObjectPropertyDef>;
158
+ readonly currentPath: ReadonlyArray<string>;
159
+ readonly sourceLocation: SourceLocation;
160
+ readonly buildContext: ContextBuilderFn;
161
+ readonly preserveDocumentation: boolean;
162
+ readonly results: InlineObjectWithContext[];
104
163
  }
105
164
 
106
- function isInputTypeName(name: string): boolean {
107
- return name.endsWith("Input");
165
+ function extractNestedInlineObjectsRecursively(
166
+ params: ExtractNestedInlineObjectsParams,
167
+ ): void {
168
+ const {
169
+ properties,
170
+ currentPath,
171
+ sourceLocation,
172
+ buildContext,
173
+ preserveDocumentation,
174
+ results,
175
+ } = params;
176
+
177
+ for (const prop of properties) {
178
+ if (
179
+ prop.tsType.kind === "inlineObject" &&
180
+ prop.tsType.inlineObjectProperties
181
+ ) {
182
+ const nestedPath = [...currentPath, prop.name];
183
+ const nestedContext = buildContext(nestedPath);
184
+ // Use property's source location if available for more accurate diagnostics
185
+ const nestedSourceLocation = prop.sourceLocation ?? sourceLocation;
186
+
187
+ results.push({
188
+ properties: prop.tsType.inlineObjectProperties,
189
+ context: nestedContext,
190
+ sourceLocation: nestedSourceLocation,
191
+ nullable: prop.tsType.nullable,
192
+ description: preserveDocumentation
193
+ ? prop.tsType.inlineObjectDescription
194
+ : null,
195
+ deprecated: preserveDocumentation
196
+ ? prop.tsType.inlineObjectDeprecated
197
+ : null,
198
+ });
199
+
200
+ extractNestedInlineObjectsRecursively({
201
+ properties: prop.tsType.inlineObjectProperties,
202
+ currentPath: nestedPath,
203
+ sourceLocation: nestedSourceLocation,
204
+ buildContext,
205
+ preserveDocumentation,
206
+ results,
207
+ });
208
+ }
209
+ }
108
210
  }
109
211
 
110
212
  function getContextKey(context: AutoTypeNameContext): string {
@@ -115,6 +217,8 @@ function getContextKey(context: AutoTypeNameContext): string {
115
217
  return `inputField:${context.parentTypeName}:${context.fieldPath.join(".")}`;
116
218
  case "resolverArg":
117
219
  return `resolverArg:${context.resolverType}:${context.parentTypeName ?? ""}:${context.fieldName}:${context.argName}:${context.fieldPath.join(".")}`;
220
+ case "resolverPayload":
221
+ return `resolverPayload:${context.resolverType}:${context.parentTypeName ?? ""}:${context.fieldName}:${context.fieldPath.join(".")}`;
118
222
  }
119
223
  }
120
224
 
@@ -128,6 +232,8 @@ function mapContextKindToGeneratedFromContext(
128
232
  return "inputField";
129
233
  case "resolverArg":
130
234
  return "resolverArg";
235
+ case "resolverPayload":
236
+ return "resolverPayload";
131
237
  }
132
238
  }
133
239
 
@@ -194,100 +300,31 @@ function collectInlineObjectsFromField(
194
300
  fieldPath,
195
301
  };
196
302
 
303
+ const sourceLocation = getSourceLocationOrDefault(
304
+ field.sourceLocation,
305
+ sourceFile,
306
+ );
307
+
197
308
  results.push({
198
309
  properties: tsType.inlineObjectProperties,
199
310
  context,
200
- sourceLocation: field.sourceLocation ?? {
201
- file: sourceFile,
202
- line: 1,
203
- column: 1,
204
- },
311
+ sourceLocation,
205
312
  nullable: tsType.nullable,
313
+ description: tsType.inlineObjectDescription,
314
+ deprecated: tsType.inlineObjectDeprecated,
206
315
  });
207
316
 
208
- for (const prop of tsType.inlineObjectProperties) {
209
- if (
210
- prop.tsType.kind === "inlineObject" &&
211
- prop.tsType.inlineObjectProperties
212
- ) {
213
- const nestedPath = [...fieldPath, prop.name];
214
- const nestedContext: AutoTypeNameContext = isInput
215
- ? {
216
- kind: "inputField",
217
- parentTypeName,
218
- fieldPath: nestedPath,
219
- }
220
- : {
221
- kind: "objectField",
222
- parentTypeName,
223
- fieldPath: nestedPath,
224
- };
225
-
226
- extractNestedInlineObjects(
227
- prop.tsType.inlineObjectProperties,
228
- nestedContext,
229
- nestedPath,
230
- parentTypeName,
231
- isInput,
232
- sourceFile,
233
- prop.sourceLocation,
234
- results,
235
- );
236
- }
237
- }
238
- }
239
-
240
- function extractNestedInlineObjects(
241
- properties: ReadonlyArray<InlineObjectPropertyDef>,
242
- context: AutoTypeNameContext,
243
- currentPath: ReadonlyArray<string>,
244
- parentTypeName: string,
245
- isInput: boolean,
246
- sourceFile: string,
247
- parentSourceLocation: SourceLocation | null,
248
- results: InlineObjectWithContext[],
249
- ): void {
250
- results.push({
251
- properties,
252
- context,
253
- sourceLocation: parentSourceLocation ?? {
254
- file: sourceFile,
255
- line: 1,
256
- column: 1,
257
- },
258
- nullable: false,
317
+ extractNestedInlineObjectsRecursively({
318
+ properties: tsType.inlineObjectProperties,
319
+ currentPath: fieldPath,
320
+ sourceLocation,
321
+ buildContext: (nestedPath) =>
322
+ isInput
323
+ ? { kind: "inputField", parentTypeName, fieldPath: nestedPath }
324
+ : { kind: "objectField", parentTypeName, fieldPath: nestedPath },
325
+ preserveDocumentation: true,
326
+ results,
259
327
  });
260
-
261
- for (const prop of properties) {
262
- if (
263
- prop.tsType.kind === "inlineObject" &&
264
- prop.tsType.inlineObjectProperties
265
- ) {
266
- const nestedPath = [...currentPath, prop.name];
267
- const nestedContext: AutoTypeNameContext = isInput
268
- ? {
269
- kind: "inputField",
270
- parentTypeName,
271
- fieldPath: nestedPath,
272
- }
273
- : {
274
- kind: "objectField",
275
- parentTypeName,
276
- fieldPath: nestedPath,
277
- };
278
-
279
- extractNestedInlineObjects(
280
- prop.tsType.inlineObjectProperties,
281
- nestedContext,
282
- nestedPath,
283
- parentTypeName,
284
- isInput,
285
- sourceFile,
286
- prop.sourceLocation,
287
- results,
288
- );
289
- }
290
- }
291
328
  }
292
329
 
293
330
  function collectInlineObjectsFromResolvers(
@@ -295,24 +332,17 @@ function collectInlineObjectsFromResolvers(
295
332
  ): InlineObjectWithContext[] {
296
333
  const results: InlineObjectWithContext[] = [];
297
334
 
298
- for (const field of resolversResult.queryFields.fields) {
299
- collectInlineObjectsFromResolverArgs(field, "query", null, results);
300
- }
301
-
302
- for (const field of resolversResult.mutationFields.fields) {
303
- collectInlineObjectsFromResolverArgs(field, "mutation", null, results);
304
- }
305
-
306
- for (const ext of resolversResult.typeExtensions) {
307
- for (const field of ext.fields) {
335
+ forEachResolverField(
336
+ resolversResult,
337
+ ({ field, resolverType, parentTypeName }) => {
308
338
  collectInlineObjectsFromResolverArgs(
309
339
  field,
310
- "field",
311
- ext.targetTypeName,
340
+ resolverType,
341
+ parentTypeName,
312
342
  results,
313
343
  );
314
- }
315
- }
344
+ },
345
+ );
316
346
 
317
347
  return results;
318
348
  }
@@ -342,65 +372,87 @@ function collectInlineObjectsFromResolverArgs(
342
372
  context,
343
373
  sourceLocation: field.sourceLocation,
344
374
  nullable: arg.type.nullable,
375
+ description: null,
376
+ deprecated: null,
345
377
  });
346
378
 
347
- extractNestedInlineObjectsFromArg(
348
- arg.inlineObjectProperties,
349
- resolverType,
350
- field.name,
351
- arg.name,
352
- parentTypeName,
353
- [],
354
- field.sourceLocation,
355
- results,
356
- );
357
- }
358
- }
359
-
360
- function extractNestedInlineObjectsFromArg(
361
- properties: ReadonlyArray<InlineObjectPropertyDef>,
362
- resolverType: "query" | "mutation" | "field",
363
- fieldName: string,
364
- argName: string,
365
- parentTypeName: string | null,
366
- currentPath: ReadonlyArray<string>,
367
- sourceLocation: SourceLocation,
368
- results: InlineObjectWithContext[],
369
- ): void {
370
- for (const prop of properties) {
371
- if (
372
- prop.tsType.kind === "inlineObject" &&
373
- prop.tsType.inlineObjectProperties
374
- ) {
375
- const nestedPath = [...currentPath, prop.name];
376
- const nestedContext: AutoTypeNameContext = {
379
+ extractNestedInlineObjectsRecursively({
380
+ properties: arg.inlineObjectProperties,
381
+ currentPath: [],
382
+ sourceLocation: field.sourceLocation,
383
+ buildContext: (nestedPath) => ({
377
384
  kind: "resolverArg",
378
385
  resolverType,
379
- fieldName,
380
- argName,
386
+ fieldName: field.name,
387
+ argName: arg.name,
381
388
  parentTypeName,
382
389
  fieldPath: nestedPath,
383
- };
390
+ }),
391
+ preserveDocumentation: false,
392
+ results,
393
+ });
394
+ }
395
+ }
384
396
 
385
- results.push({
386
- properties: prop.tsType.inlineObjectProperties,
387
- context: nestedContext,
388
- sourceLocation,
389
- nullable: prop.tsType.nullable,
390
- });
397
+ function collectInlinePayloadsFromResolvers(
398
+ resolversResult: ExtractResolversResult,
399
+ ): InlineObjectWithContext[] {
400
+ const results: InlineObjectWithContext[] = [];
391
401
 
392
- extractNestedInlineObjectsFromArg(
393
- prop.tsType.inlineObjectProperties,
402
+ forEachResolverField(
403
+ resolversResult,
404
+ ({ field, resolverType, parentTypeName }) => {
405
+ collectInlinePayloadFromReturnType(
406
+ field,
394
407
  resolverType,
395
- fieldName,
396
- argName,
397
408
  parentTypeName,
398
- nestedPath,
399
- sourceLocation,
400
409
  results,
401
410
  );
402
- }
403
- }
411
+ },
412
+ );
413
+
414
+ return results;
415
+ }
416
+
417
+ function collectInlinePayloadFromReturnType(
418
+ field: GraphQLFieldDefinition,
419
+ resolverType: "query" | "mutation" | "field",
420
+ parentTypeName: string | null,
421
+ results: InlineObjectWithContext[],
422
+ ): void {
423
+ if (!field.returnTypeInlineObjectProperties) return;
424
+
425
+ const context: AutoTypeNameContext = {
426
+ kind: "resolverPayload",
427
+ resolverType,
428
+ fieldName: field.name,
429
+ parentTypeName,
430
+ fieldPath: [],
431
+ };
432
+
433
+ results.push({
434
+ properties: field.returnTypeInlineObjectProperties,
435
+ context,
436
+ sourceLocation: field.sourceLocation,
437
+ nullable: field.type.nullable,
438
+ description: field.returnTypeInlineObjectDescription,
439
+ deprecated: field.returnTypeInlineObjectDeprecated,
440
+ });
441
+
442
+ extractNestedInlineObjectsRecursively({
443
+ properties: field.returnTypeInlineObjectProperties,
444
+ currentPath: [],
445
+ sourceLocation: field.sourceLocation,
446
+ buildContext: (nestedPath) => ({
447
+ kind: "resolverPayload",
448
+ resolverType,
449
+ fieldName: field.name,
450
+ parentTypeName,
451
+ fieldPath: nestedPath,
452
+ }),
453
+ preserveDocumentation: true,
454
+ results,
455
+ });
404
456
  }
405
457
 
406
458
  interface GenerateAutoTypeResult {
@@ -412,12 +464,14 @@ interface GenerateAutoTypeParams {
412
464
  readonly inlineObj: InlineObjectWithContext;
413
465
  readonly generatedTypeNames: Map<string, string>;
414
466
  readonly enumTypeNames: Map<string, string>;
467
+ readonly unionTypeNames: Map<string, string>;
415
468
  }
416
469
 
417
470
  function generateAutoType(
418
471
  params: GenerateAutoTypeParams,
419
472
  ): GenerateAutoTypeResult {
420
- const { inlineObj, generatedTypeNames, enumTypeNames } = params;
473
+ const { inlineObj, generatedTypeNames, enumTypeNames, unionTypeNames } =
474
+ params;
421
475
  const name = generateAutoTypeName(inlineObj.context);
422
476
  const isInput =
423
477
  inlineObj.context.kind === "inputField" ||
@@ -445,6 +499,7 @@ function generateAutoType(
445
499
  prop,
446
500
  generatedTypeNames,
447
501
  enumTypeNames,
502
+ unionTypeNames,
448
503
  parentContext: inlineObj.context,
449
504
  });
450
505
  fields.push({
@@ -463,10 +518,12 @@ function generateAutoType(
463
518
  kind: isInput ? "InputObject" : "Object",
464
519
  fields,
465
520
  enumValues: null,
521
+ unionMembers: null,
466
522
  needsStringEnumMapping: false,
467
523
  sourceLocation: inlineObj.sourceLocation,
468
524
  generatedFrom: buildGeneratedFromInfo(inlineObj.context),
469
- description: null,
525
+ description: inlineObj.description,
526
+ resolveTypeFieldPattern: null,
470
527
  },
471
528
  diagnostics,
472
529
  };
@@ -476,49 +533,63 @@ interface ResolveFieldTypeParams {
476
533
  readonly prop: InlineObjectPropertyDef;
477
534
  readonly generatedTypeNames: Map<string, string>;
478
535
  readonly enumTypeNames: Map<string, string>;
536
+ readonly unionTypeNames: Map<string, string>;
479
537
  readonly parentContext: AutoTypeNameContext;
480
538
  }
481
539
 
540
+ function tryResolveNestedType(
541
+ prop: InlineObjectPropertyDef,
542
+ parentContext: AutoTypeNameContext,
543
+ typeNamesMap: ReadonlyMap<string, string>,
544
+ ): GraphQLFieldType | null {
545
+ const nestedPath = [...parentContext.fieldPath, prop.name];
546
+ const nestedContext: AutoTypeNameContext = {
547
+ ...parentContext,
548
+ fieldPath: nestedPath,
549
+ };
550
+ const contextKey = getContextKey(nestedContext);
551
+ const resolvedTypeName = typeNamesMap.get(contextKey);
552
+
553
+ if (resolvedTypeName) {
554
+ return {
555
+ typeName: resolvedTypeName,
556
+ nullable: prop.tsType.nullable || prop.optional,
557
+ list: false,
558
+ listItemNullable: null,
559
+ };
560
+ }
561
+ return null;
562
+ }
563
+
482
564
  function resolveFieldType(params: ResolveFieldTypeParams): GraphQLFieldType {
483
- const { prop, generatedTypeNames, enumTypeNames, parentContext } = params;
565
+ const {
566
+ prop,
567
+ generatedTypeNames,
568
+ enumTypeNames,
569
+ unionTypeNames,
570
+ parentContext,
571
+ } = params;
484
572
 
485
573
  if (
486
574
  prop.tsType.kind === "inlineObject" &&
487
575
  prop.tsType.inlineObjectProperties
488
576
  ) {
489
- const nestedPath = [...parentContext.fieldPath, prop.name];
490
- const nestedContext: AutoTypeNameContext = {
491
- ...parentContext,
492
- fieldPath: nestedPath,
493
- };
494
- const contextKey = getContextKey(nestedContext);
495
- const resolvedTypeName = generatedTypeNames.get(contextKey);
496
- if (resolvedTypeName) {
497
- return {
498
- typeName: resolvedTypeName,
499
- nullable: prop.tsType.nullable || prop.optional,
500
- list: false,
501
- listItemNullable: null,
502
- };
503
- }
577
+ const result = tryResolveNestedType(
578
+ prop,
579
+ parentContext,
580
+ generatedTypeNames,
581
+ );
582
+ if (result) return result;
504
583
  }
505
584
 
506
585
  if (prop.tsType.kind === "inlineEnum" && prop.tsType.inlineEnumMembers) {
507
- const nestedPath = [...parentContext.fieldPath, prop.name];
508
- const nestedContext: AutoTypeNameContext = {
509
- ...parentContext,
510
- fieldPath: nestedPath,
511
- };
512
- const contextKey = getContextKey(nestedContext);
513
- const resolvedTypeName = enumTypeNames.get(contextKey);
514
- if (resolvedTypeName) {
515
- return {
516
- typeName: resolvedTypeName,
517
- nullable: prop.tsType.nullable || prop.optional,
518
- list: false,
519
- listItemNullable: null,
520
- };
521
- }
586
+ const result = tryResolveNestedType(prop, parentContext, enumTypeNames);
587
+ if (result) return result;
588
+ }
589
+
590
+ if (prop.tsType.kind === "union" && prop.tsType.members) {
591
+ const result = tryResolveNestedType(prop, parentContext, unionTypeNames);
592
+ if (result) return result;
522
593
  }
523
594
 
524
595
  return convertTsTypeToGraphQLType(prop.tsType, prop.optional);
@@ -527,6 +598,7 @@ function resolveFieldType(params: ResolveFieldTypeParams): GraphQLFieldType {
527
598
  interface UpdateTypeNamesParams {
528
599
  readonly generatedTypeNames: Map<string, string>;
529
600
  readonly enumTypeNames: Map<string, string>;
601
+ readonly unionTypeNames: Map<string, string>;
530
602
  }
531
603
 
532
604
  function updateExtractedTypes(
@@ -550,7 +622,7 @@ function updateField(
550
622
  parentTypeName: string,
551
623
  isInput: boolean,
552
624
  ): FieldDefinition {
553
- const { generatedTypeNames, enumTypeNames } = params;
625
+ const { generatedTypeNames, enumTypeNames, unionTypeNames } = params;
554
626
  const context = buildFieldContext(parentTypeName, [field.name], isInput);
555
627
  const contextKey = getContextKey(context);
556
628
 
@@ -606,6 +678,20 @@ function updateField(
606
678
  }
607
679
  }
608
680
 
681
+ // Handle inline union types
682
+ if (field.tsType.kind === "union" && field.tsType.members) {
683
+ const resolvedTypeName = unionTypeNames.get(contextKey);
684
+ if (resolvedTypeName) {
685
+ return {
686
+ ...field,
687
+ tsType: createReferenceType({
688
+ name: resolvedTypeName,
689
+ nullable: field.tsType.nullable,
690
+ }),
691
+ };
692
+ }
693
+ }
694
+
609
695
  return field;
610
696
  }
611
697
 
@@ -640,9 +726,55 @@ function updateResolverField(
640
726
  resolverType: "query" | "mutation" | "field",
641
727
  parentTypeName: string | null,
642
728
  ): GraphQLFieldDefinition {
643
- if (!field.args) return field;
729
+ const { generatedTypeNames, enumTypeNames, unionTypeNames } = params;
644
730
 
645
- const { generatedTypeNames, enumTypeNames } = params;
731
+ let updatedType = field.type;
732
+
733
+ // Create payload context once for all inline return type checks
734
+ const hasInlinePayload =
735
+ field.returnTypeInlineObjectProperties ||
736
+ field.returnTypeInlineEnumMembers ||
737
+ field.returnTypeInlineUnionMembers;
738
+
739
+ if (hasInlinePayload) {
740
+ const payloadContext: AutoTypeNameContext = {
741
+ kind: "resolverPayload",
742
+ resolverType,
743
+ fieldName: field.name,
744
+ parentTypeName,
745
+ fieldPath: [],
746
+ };
747
+ const payloadContextKey = getContextKey(payloadContext);
748
+
749
+ // These are mutually exclusive - a return type can only be one of:
750
+ // inline object, inline enum, or inline union
751
+ if (field.returnTypeInlineObjectProperties) {
752
+ // Handle inline payload objects in return type
753
+ const resolvedTypeName = generatedTypeNames.get(payloadContextKey);
754
+ if (resolvedTypeName) {
755
+ updatedType = { ...field.type, typeName: resolvedTypeName };
756
+ }
757
+ } else if (field.returnTypeInlineEnumMembers) {
758
+ // Handle inline enum in return type
759
+ const resolvedTypeName = enumTypeNames.get(payloadContextKey);
760
+ if (resolvedTypeName) {
761
+ updatedType = { ...field.type, typeName: resolvedTypeName };
762
+ }
763
+ } else if (field.returnTypeInlineUnionMembers) {
764
+ // Handle inline union in return type
765
+ const resolvedTypeName = unionTypeNames.get(payloadContextKey);
766
+ if (resolvedTypeName) {
767
+ updatedType = { ...field.type, typeName: resolvedTypeName };
768
+ }
769
+ }
770
+ }
771
+
772
+ if (!field.args) {
773
+ return {
774
+ ...field,
775
+ type: updatedType,
776
+ };
777
+ }
646
778
 
647
779
  const updatedArgs = field.args.map((arg) => {
648
780
  const context: AutoTypeNameContext = {
@@ -683,11 +815,26 @@ function updateResolverField(
683
815
  }
684
816
  }
685
817
 
818
+ // Handle inline unions (OneOf input objects)
819
+ if (arg.inlineUnionMembers) {
820
+ const resolvedTypeName = unionTypeNames.get(contextKey);
821
+ if (resolvedTypeName) {
822
+ return {
823
+ ...arg,
824
+ type: {
825
+ ...arg.type,
826
+ typeName: resolvedTypeName,
827
+ },
828
+ };
829
+ }
830
+ }
831
+
686
832
  return arg;
687
833
  });
688
834
 
689
835
  return {
690
836
  ...field,
837
+ type: updatedType,
691
838
  args: updatedArgs,
692
839
  };
693
840
  }
@@ -726,6 +873,11 @@ interface ConvertEnumMembersWithDiagnosticsResult {
726
873
  readonly diagnostics: ReadonlyArray<Diagnostic>;
727
874
  }
728
875
 
876
+ interface ConvertedMemberInfo {
877
+ readonly convertedName: string;
878
+ readonly member: InlineEnumMemberInfo;
879
+ }
880
+
729
881
  function convertInlineEnumMembers(
730
882
  params: ConvertInlineEnumMembersParams,
731
883
  ): ConvertEnumMembersWithDiagnosticsResult {
@@ -734,32 +886,46 @@ function convertInlineEnumMembers(
734
886
  const enumValues: AutoGeneratedEnumValue[] = [];
735
887
  const diagnostics: Diagnostic[] = [];
736
888
 
737
- const convertedNameToOriginals = new Map<string, string[]>();
889
+ const convertedMembers: ConvertedMemberInfo[] = [];
738
890
 
739
891
  for (const member of members) {
740
892
  const convertedName = toScreamingSnakeCase(member.value);
893
+ convertedMembers.push({ convertedName, member });
894
+ }
895
+
896
+ const prefixDetectionResult = detectEnumPrefix({
897
+ enumName,
898
+ memberValues: convertedMembers.map((m) => m.convertedName),
899
+ });
900
+
901
+ const finalNameToOriginals = new Map<string, string[]>();
741
902
 
742
- if (convertedName !== member.value) {
903
+ for (const { convertedName, member } of convertedMembers) {
904
+ let finalName = convertedName;
905
+ if (prefixDetectionResult.shouldStrip && prefixDetectionResult.prefix) {
906
+ finalName = stripEnumPrefix(convertedName, prefixDetectionResult.prefix);
907
+ needsStringEnumMapping = true;
908
+ } else if (convertedName !== member.value) {
743
909
  needsStringEnumMapping = true;
744
910
  }
745
911
 
746
- const originals = convertedNameToOriginals.get(convertedName) ?? [];
912
+ const originals = finalNameToOriginals.get(finalName) ?? [];
747
913
  originals.push(member.value);
748
- convertedNameToOriginals.set(convertedName, originals);
914
+ finalNameToOriginals.set(finalName, originals);
749
915
 
750
916
  enumValues.push({
751
- name: convertedName,
917
+ name: finalName,
752
918
  originalValue: member.value,
753
919
  description: member.description,
754
920
  deprecated: member.deprecated,
755
921
  });
756
922
  }
757
923
 
758
- for (const [convertedName, originals] of convertedNameToOriginals) {
924
+ for (const [finalName, originals] of finalNameToOriginals) {
759
925
  if (originals.length > 1) {
760
926
  diagnostics.push({
761
927
  code: "DUPLICATE_ENUM_VALUE_AFTER_CONVERSION",
762
- message: `Enum '${enumName}' has duplicate value '${convertedName}' after conversion (from '${originals.join("' and '")}')`,
928
+ message: `Enum '${enumName}' has duplicate value '${finalName}' after conversion (from '${originals.join("' and '")}')`,
763
929
  severity: "error",
764
930
  location: sourceLocation,
765
931
  });
@@ -791,10 +957,12 @@ function generateAutoEnumType(
791
957
  kind: "Enum",
792
958
  fields: null,
793
959
  enumValues,
960
+ unionMembers: null,
794
961
  needsStringEnumMapping,
795
962
  sourceLocation: inlineEnum.sourceLocation,
796
963
  generatedFrom: buildGeneratedFromInfo(inlineEnum.context),
797
964
  description: inlineEnum.externalEnumDescription,
965
+ resolveTypeFieldPattern: null,
798
966
  },
799
967
  diagnostics,
800
968
  };
@@ -850,6 +1018,534 @@ function buildEnumTypeNamesMap(
850
1018
  };
851
1019
  }
852
1020
 
1021
+ function buildUnionTypeNamesMap(
1022
+ inlineUnions: InlineUnionWithContext[],
1023
+ ): Map<string, string> {
1024
+ const unionTypeNames = new Map<string, string>();
1025
+
1026
+ for (const inlineUnion of inlineUnions) {
1027
+ const contextKey = getContextKey(inlineUnion.context);
1028
+ const typeName = generateAutoTypeName(inlineUnion.context);
1029
+ unionTypeNames.set(contextKey, typeName);
1030
+ }
1031
+
1032
+ return unionTypeNames;
1033
+ }
1034
+
1035
+ interface ProcessInlineUnionsParams {
1036
+ readonly inlineUnions: ReadonlyArray<InlineUnionWithContext>;
1037
+ readonly knownTypeNames: ReadonlySet<string>;
1038
+ readonly generatedTypeNames: Map<string, string>;
1039
+ readonly enumTypeNames: Map<string, string>;
1040
+ readonly unionTypeNames: Map<string, string>;
1041
+ readonly extractedTypes: ReadonlyArray<ExtractedTypeInfo>;
1042
+ }
1043
+
1044
+ interface ProcessInlineUnionsResult {
1045
+ readonly types: AutoGeneratedType[];
1046
+ readonly diagnostics: Diagnostic[];
1047
+ }
1048
+
1049
+ interface ProcessOneOfInputObjectsParams {
1050
+ readonly inlineUnions: ReadonlyArray<InlineUnionWithContext>;
1051
+ readonly knownTypeNames: ReadonlySet<string>;
1052
+ readonly typeMap: ReadonlyMap<string, ExtractedTypeInfo>;
1053
+ readonly unionTypeNames: Map<string, string>;
1054
+ }
1055
+
1056
+ function processOneOfInputObjects(
1057
+ params: ProcessOneOfInputObjectsParams,
1058
+ ): ProcessInlineUnionsResult {
1059
+ const { inlineUnions, knownTypeNames, typeMap, unionTypeNames } = params;
1060
+ const types: AutoGeneratedType[] = [];
1061
+ const diagnostics: Diagnostic[] = [];
1062
+
1063
+ for (const inlineUnion of inlineUnions) {
1064
+ const contextKey = getContextKey(inlineUnion.context);
1065
+ const typeName = generateAutoTypeName(inlineUnion.context);
1066
+
1067
+ const validationResult = validateOneOfMembers({
1068
+ members: inlineUnion.members,
1069
+ typeName,
1070
+ sourceLocation: inlineUnion.sourceLocation,
1071
+ typeMap,
1072
+ });
1073
+
1074
+ diagnostics.push(...validationResult.diagnostics);
1075
+
1076
+ if (!validationResult.valid) {
1077
+ continue;
1078
+ }
1079
+
1080
+ const fields = generateOneOfFields({
1081
+ members: inlineUnion.members,
1082
+ knownTypeNames,
1083
+ });
1084
+
1085
+ types.push({
1086
+ name: typeName,
1087
+ kind: "OneOfInputObject",
1088
+ fields,
1089
+ enumValues: null,
1090
+ unionMembers: null,
1091
+ needsStringEnumMapping: false,
1092
+ sourceLocation: inlineUnion.sourceLocation,
1093
+ generatedFrom: buildGeneratedFromInfo(inlineUnion.context),
1094
+ description: null,
1095
+ resolveTypeFieldPattern: null,
1096
+ });
1097
+
1098
+ unionTypeNames.set(contextKey, typeName);
1099
+ }
1100
+
1101
+ return { types, diagnostics };
1102
+ }
1103
+
1104
+ interface ProcessUnionTypesParams {
1105
+ readonly inlineUnions: ReadonlyArray<InlineUnionWithContext>;
1106
+ readonly knownTypeNames: ReadonlySet<string>;
1107
+ readonly generatedTypeNames: Map<string, string>;
1108
+ readonly enumTypeNames: Map<string, string>;
1109
+ readonly unionTypeNames: Map<string, string>;
1110
+ readonly typeMap: ReadonlyMap<string, ExtractedTypeInfo>;
1111
+ }
1112
+
1113
+ function processUnionTypes(
1114
+ params: ProcessUnionTypesParams,
1115
+ ): ProcessInlineUnionsResult {
1116
+ const {
1117
+ inlineUnions,
1118
+ knownTypeNames,
1119
+ generatedTypeNames,
1120
+ enumTypeNames,
1121
+ unionTypeNames,
1122
+ typeMap,
1123
+ } = params;
1124
+ const types: AutoGeneratedType[] = [];
1125
+ const diagnostics: Diagnostic[] = [];
1126
+ const generatedTypenameTypes = new Map<
1127
+ string,
1128
+ ReadonlyArray<AutoGeneratedField>
1129
+ >();
1130
+
1131
+ for (const inlineUnion of inlineUnions) {
1132
+ const contextKey = getContextKey(inlineUnion.context);
1133
+ const typeName = generateAutoTypeName(inlineUnion.context);
1134
+
1135
+ const validationResult = validateUnionMembers({
1136
+ members: inlineUnion.members,
1137
+ typeName,
1138
+ sourceLocation: inlineUnion.sourceLocation,
1139
+ typeMap,
1140
+ });
1141
+
1142
+ diagnostics.push(...validationResult.diagnostics);
1143
+
1144
+ if (!validationResult.valid) {
1145
+ continue;
1146
+ }
1147
+
1148
+ let resolveTypeFieldPattern: AutoGeneratedType["resolveTypeFieldPattern"] =
1149
+ null;
1150
+
1151
+ if (inlineUnion.context.kind === "resolverPayload") {
1152
+ const typenameValidationResult = validateUnionMemberTypenames({
1153
+ members: inlineUnion.members,
1154
+ unionTypeName: typeName,
1155
+ sourceLocation: inlineUnion.sourceLocation,
1156
+ typeMap,
1157
+ });
1158
+
1159
+ diagnostics.push(...typenameValidationResult.diagnostics);
1160
+
1161
+ if (!typenameValidationResult.valid) {
1162
+ continue;
1163
+ }
1164
+
1165
+ if (typenameValidationResult.allMembersHaveTypename) {
1166
+ resolveTypeFieldPattern = determineFieldPattern(
1167
+ typenameValidationResult.memberTypenames,
1168
+ );
1169
+ }
1170
+ }
1171
+
1172
+ const memberNames = resolveMemberNames({
1173
+ members: inlineUnion.members,
1174
+ knownTypeNames,
1175
+ generatedTypeNames,
1176
+ enumTypeNames,
1177
+ parentContext: inlineUnion.context,
1178
+ unionTypeNames,
1179
+ types,
1180
+ diagnostics,
1181
+ generatedTypenameTypes,
1182
+ sourceLocation: inlineUnion.sourceLocation,
1183
+ });
1184
+
1185
+ types.push({
1186
+ name: typeName,
1187
+ kind: "Union",
1188
+ fields: null,
1189
+ enumValues: null,
1190
+ unionMembers: memberNames,
1191
+ needsStringEnumMapping: false,
1192
+ sourceLocation: inlineUnion.sourceLocation,
1193
+ generatedFrom: buildGeneratedFromInfo(inlineUnion.context),
1194
+ description: null,
1195
+ resolveTypeFieldPattern,
1196
+ });
1197
+
1198
+ unionTypeNames.set(contextKey, typeName);
1199
+ }
1200
+
1201
+ return { types, diagnostics };
1202
+ }
1203
+
1204
+ /**
1205
+ * Determine the field pattern for resolveType based on the typename fields used by members.
1206
+ */
1207
+ function determineFieldPattern(
1208
+ memberTypenames: ReadonlyMap<number, ValidatedTypenameInfo>,
1209
+ ): ResolveTypeFieldPattern {
1210
+ const fieldNames = new Set<TypenameFieldName>();
1211
+ for (const info of memberTypenames.values()) {
1212
+ fieldNames.add(info.fieldName);
1213
+ }
1214
+
1215
+ if (fieldNames.size === 0) {
1216
+ return { usedFieldNames: createFieldNameSet(["__typename"]) };
1217
+ }
1218
+
1219
+ return { usedFieldNames: fieldNames };
1220
+ }
1221
+
1222
+ function processInlineUnions(
1223
+ params: ProcessInlineUnionsParams,
1224
+ ): ProcessInlineUnionsResult {
1225
+ const {
1226
+ inlineUnions,
1227
+ knownTypeNames,
1228
+ generatedTypeNames,
1229
+ enumTypeNames,
1230
+ unionTypeNames,
1231
+ extractedTypes,
1232
+ } = params;
1233
+
1234
+ const typeMap = new Map<string, ExtractedTypeInfo>();
1235
+ for (const typeInfo of extractedTypes) {
1236
+ typeMap.set(typeInfo.metadata.name, typeInfo);
1237
+ }
1238
+
1239
+ const inputUnions = inlineUnions.filter((u) => u.isInputContext);
1240
+ const outputUnions = inlineUnions.filter((u) => !u.isInputContext);
1241
+
1242
+ const oneOfResult = processOneOfInputObjects({
1243
+ inlineUnions: inputUnions,
1244
+ knownTypeNames,
1245
+ typeMap,
1246
+ unionTypeNames,
1247
+ });
1248
+
1249
+ const unionResult = processUnionTypes({
1250
+ inlineUnions: outputUnions,
1251
+ knownTypeNames,
1252
+ generatedTypeNames,
1253
+ enumTypeNames,
1254
+ unionTypeNames,
1255
+ typeMap,
1256
+ });
1257
+
1258
+ return {
1259
+ types: [...oneOfResult.types, ...unionResult.types],
1260
+ diagnostics: [...oneOfResult.diagnostics, ...unionResult.diagnostics],
1261
+ };
1262
+ }
1263
+
1264
+ interface GenerateOneOfFieldsParams {
1265
+ readonly members: ReadonlyArray<InlineUnionMemberInfo>;
1266
+ readonly knownTypeNames: ReadonlySet<string>;
1267
+ }
1268
+
1269
+ function generateOneOfFields(
1270
+ params: GenerateOneOfFieldsParams,
1271
+ ): AutoGeneratedField[] {
1272
+ const { members, knownTypeNames } = params;
1273
+
1274
+ const fields: AutoGeneratedField[] = [];
1275
+
1276
+ for (const member of members) {
1277
+ const memberType = member.memberType;
1278
+
1279
+ if (
1280
+ memberType.kind === "inlineObject" &&
1281
+ memberType.inlineObjectProperties
1282
+ ) {
1283
+ for (const prop of memberType.inlineObjectProperties) {
1284
+ const fieldType = convertTsTypeToGraphQLType(
1285
+ prop.tsType,
1286
+ prop.optional,
1287
+ );
1288
+ fields.push({
1289
+ name: prop.name,
1290
+ type: {
1291
+ ...fieldType,
1292
+ nullable: true,
1293
+ },
1294
+ description: prop.description,
1295
+ deprecated: prop.deprecated,
1296
+ directives: prop.directives,
1297
+ defaultValue: prop.defaultValue,
1298
+ });
1299
+ }
1300
+ } else if (memberType.kind === "reference" && memberType.name) {
1301
+ if (knownTypeNames.has(memberType.name)) {
1302
+ const camelCaseName =
1303
+ memberType.name.charAt(0).toLowerCase() + memberType.name.slice(1);
1304
+ fields.push({
1305
+ name: camelCaseName,
1306
+ type: {
1307
+ typeName: memberType.name,
1308
+ nullable: true,
1309
+ list: false,
1310
+ listItemNullable: null,
1311
+ },
1312
+ description: null,
1313
+ deprecated: null,
1314
+ directives: null,
1315
+ defaultValue: null,
1316
+ });
1317
+ }
1318
+ }
1319
+ }
1320
+
1321
+ return fields;
1322
+ }
1323
+
1324
+ interface ResolveMemberNamesParams {
1325
+ readonly members: ReadonlyArray<InlineUnionMemberInfo>;
1326
+ readonly knownTypeNames: ReadonlySet<string>;
1327
+ readonly generatedTypeNames: Map<string, string>;
1328
+ readonly enumTypeNames: Map<string, string>;
1329
+ readonly parentContext: AutoTypeNameContext;
1330
+ readonly unionTypeNames: Map<string, string>;
1331
+ readonly types: AutoGeneratedType[];
1332
+ readonly diagnostics: Diagnostic[];
1333
+ readonly generatedTypenameTypes: Map<
1334
+ string,
1335
+ ReadonlyArray<AutoGeneratedField>
1336
+ >;
1337
+ readonly sourceLocation: SourceLocation;
1338
+ }
1339
+
1340
+ /**
1341
+ * Extract __typename or $typeName property value from inline object properties.
1342
+ * Returns null if neither is found or neither is a valid string literal.
1343
+ * __typename takes priority over $typeName if both are present.
1344
+ */
1345
+ function extractTypenameFromInlineObject(
1346
+ properties: ReadonlyArray<InlineObjectPropertyDef>,
1347
+ ): TypenameFieldInfo | null {
1348
+ const found = findTypenameProperty(properties, (p) => p.name);
1349
+ if (!found) {
1350
+ return null;
1351
+ }
1352
+
1353
+ const { property, fieldName } = found;
1354
+ const { tsType } = property;
1355
+
1356
+ if (tsType.kind === "literal" && tsType.name !== null) {
1357
+ return { typeName: tsType.name, fieldName };
1358
+ }
1359
+
1360
+ return null;
1361
+ }
1362
+
1363
+ function resolveMemberNames(params: ResolveMemberNamesParams): string[] {
1364
+ const {
1365
+ members,
1366
+ generatedTypeNames,
1367
+ parentContext,
1368
+ types,
1369
+ generatedTypenameTypes,
1370
+ diagnostics,
1371
+ sourceLocation,
1372
+ } = params;
1373
+
1374
+ const memberNames: string[] = [];
1375
+
1376
+ for (let i = 0; i < members.length; i++) {
1377
+ const member = members[i]!;
1378
+ const memberType = member.memberType;
1379
+
1380
+ if (memberType.kind === "reference" && memberType.name) {
1381
+ memberNames.push(memberType.name);
1382
+ } else if (
1383
+ memberType.kind === "inlineObject" &&
1384
+ memberType.inlineObjectProperties
1385
+ ) {
1386
+ // Only extract __typename or $typeName for resolverPayload context
1387
+ // For other contexts (resolverArg, objectField, inputField), these fields
1388
+ // should be treated as regular fields, not as type discriminators
1389
+ const extractedInfo =
1390
+ parentContext.kind === "resolverPayload"
1391
+ ? extractTypenameFromInlineObject(memberType.inlineObjectProperties)
1392
+ : null;
1393
+
1394
+ let memberTypeName: string;
1395
+ let contextKey: string;
1396
+
1397
+ if (extractedInfo !== null) {
1398
+ memberTypeName = extractedInfo.typeName;
1399
+ const nestedContext: AutoTypeNameContext = {
1400
+ ...parentContext,
1401
+ fieldPath: [...parentContext.fieldPath, extractedInfo.typeName],
1402
+ };
1403
+ contextKey = getContextKey(nestedContext);
1404
+ } else {
1405
+ const nestedContext: AutoTypeNameContext = {
1406
+ ...parentContext,
1407
+ fieldPath: [...parentContext.fieldPath, `member${i}`],
1408
+ };
1409
+ memberTypeName = generateAutoTypeName(nestedContext);
1410
+ contextKey = getContextKey(nestedContext);
1411
+ }
1412
+
1413
+ // Only filter out __typename or $typeName for resolverPayload context
1414
+ // For other contexts, these are regular fields that should be preserved
1415
+ const typenameFieldToFilter = extractedInfo?.fieldName ?? null;
1416
+ const fields: AutoGeneratedField[] = memberType.inlineObjectProperties
1417
+ .filter(
1418
+ (prop) =>
1419
+ parentContext.kind !== "resolverPayload" ||
1420
+ prop.name !== typenameFieldToFilter,
1421
+ )
1422
+ .map((prop) => {
1423
+ const fieldType = convertTsTypeToGraphQLType(
1424
+ prop.tsType,
1425
+ prop.optional,
1426
+ );
1427
+ return {
1428
+ name: prop.name,
1429
+ type: fieldType,
1430
+ description: prop.description,
1431
+ deprecated: prop.deprecated,
1432
+ directives: prop.directives,
1433
+ defaultValue: prop.defaultValue,
1434
+ };
1435
+ });
1436
+
1437
+ // Check if this typename was already seen with a different field structure
1438
+ if (extractedInfo !== null) {
1439
+ const existingFields = generatedTypenameTypes.get(
1440
+ extractedInfo.typeName,
1441
+ );
1442
+ if (existingFields !== undefined) {
1443
+ // Compare field structures
1444
+ if (!areFieldStructuresEqual(existingFields, fields)) {
1445
+ diagnostics.push({
1446
+ code: "TYPENAME_FIELD_STRUCTURE_MISMATCH",
1447
+ message: `Type with ${extractedInfo.fieldName} '${extractedInfo.typeName}' has inconsistent field structures across different union members. All types with the same ${extractedInfo.fieldName} must have identical field definitions.`,
1448
+ severity: "error",
1449
+ location: sourceLocation,
1450
+ });
1451
+ }
1452
+ // Skip generating duplicate type and avoid adding duplicate union member
1453
+ if (!memberNames.includes(memberTypeName)) {
1454
+ memberNames.push(memberTypeName);
1455
+ }
1456
+ continue;
1457
+ }
1458
+ }
1459
+
1460
+ const nestedContext: AutoTypeNameContext = {
1461
+ ...parentContext,
1462
+ fieldPath: [
1463
+ ...parentContext.fieldPath,
1464
+ extractedInfo?.typeName ?? `member${i}`,
1465
+ ],
1466
+ };
1467
+
1468
+ types.push({
1469
+ name: memberTypeName,
1470
+ kind: "Object",
1471
+ fields,
1472
+ enumValues: null,
1473
+ unionMembers: null,
1474
+ needsStringEnumMapping: false,
1475
+ sourceLocation: {
1476
+ file: "",
1477
+ line: 1,
1478
+ column: 1,
1479
+ },
1480
+ generatedFrom: buildGeneratedFromInfo(nestedContext),
1481
+ description: memberType.inlineObjectDescription,
1482
+ resolveTypeFieldPattern: null,
1483
+ });
1484
+
1485
+ generatedTypeNames.set(contextKey, memberTypeName);
1486
+ if (extractedInfo !== null) {
1487
+ generatedTypenameTypes.set(extractedInfo.typeName, fields);
1488
+ }
1489
+ memberNames.push(memberTypeName);
1490
+ }
1491
+ }
1492
+
1493
+ return memberNames;
1494
+ }
1495
+
1496
+ /**
1497
+ * Compare two field structures for equality.
1498
+ * Returns true if both have the same fields with the same types.
1499
+ */
1500
+ function areFieldStructuresEqual(
1501
+ fields1: ReadonlyArray<AutoGeneratedField>,
1502
+ fields2: ReadonlyArray<AutoGeneratedField>,
1503
+ ): boolean {
1504
+ if (fields1.length !== fields2.length) {
1505
+ return false;
1506
+ }
1507
+
1508
+ const sorted1 = [...fields1].sort((a, b) => a.name.localeCompare(b.name));
1509
+ const sorted2 = [...fields2].sort((a, b) => a.name.localeCompare(b.name));
1510
+
1511
+ for (let i = 0; i < sorted1.length; i++) {
1512
+ const f1 = sorted1[i]!;
1513
+ const f2 = sorted2[i]!;
1514
+
1515
+ if (f1.name !== f2.name) {
1516
+ return false;
1517
+ }
1518
+
1519
+ if (!areGraphQLTypesEqual(f1.type, f2.type)) {
1520
+ return false;
1521
+ }
1522
+ }
1523
+
1524
+ return true;
1525
+ }
1526
+
1527
+ /**
1528
+ * Compare two GraphQL type references for equality.
1529
+ */
1530
+ function areGraphQLTypesEqual(
1531
+ type1: GraphQLFieldType,
1532
+ type2: GraphQLFieldType,
1533
+ ): boolean {
1534
+ if (type1.typeName !== type2.typeName) {
1535
+ return false;
1536
+ }
1537
+ if (type1.nullable !== type2.nullable) {
1538
+ return false;
1539
+ }
1540
+ if (type1.list !== type2.list) {
1541
+ return false;
1542
+ }
1543
+ if (type1.listItemNullable !== type2.listItemNullable) {
1544
+ return false;
1545
+ }
1546
+ return true;
1547
+ }
1548
+
853
1549
  export function generateAutoTypes(
854
1550
  input: AutoTypeGeneratorInput,
855
1551
  ): AutoTypeGeneratorResult {
@@ -862,9 +1558,14 @@ export function generateAutoTypes(
862
1558
  input.resolversResult,
863
1559
  );
864
1560
 
1561
+ const inlinePayloadsFromResolvers = collectInlinePayloadsFromResolvers(
1562
+ input.resolversResult,
1563
+ );
1564
+
865
1565
  const allInlineObjects = [
866
1566
  ...inlineObjectsFromTypes,
867
1567
  ...inlineObjectsFromResolvers,
1568
+ ...inlinePayloadsFromResolvers,
868
1569
  ];
869
1570
 
870
1571
  const generatedTypeNames = buildGeneratedTypeNamesMap(allInlineObjects);
@@ -872,14 +1573,43 @@ export function generateAutoTypes(
872
1573
  const inlineEnumsFromTypes = collectInlineEnumsFromTypes(
873
1574
  input.extractedTypes,
874
1575
  );
875
- const inlineEnumsFromResolvers = collectInlineEnumsFromResolvers(
876
- input.resolversResult,
877
- );
878
- const allInlineEnums = [...inlineEnumsFromTypes, ...inlineEnumsFromResolvers];
1576
+ const inlineEnumsFromResolvers = collectInlineEnumsFromResolvers({
1577
+ resolversResult: input.resolversResult,
1578
+ });
1579
+ const inlineEnumsFromPayloads = collectInlineEnumsFromPayloads({
1580
+ resolversResult: input.resolversResult,
1581
+ });
1582
+ const allInlineEnums = [
1583
+ ...inlineEnumsFromTypes,
1584
+ ...inlineEnumsFromResolvers,
1585
+ ...inlineEnumsFromPayloads,
1586
+ ];
879
1587
 
880
1588
  const { enumTypeNames, uniqueInlineEnums } =
881
1589
  buildEnumTypeNamesMap(allInlineEnums);
882
1590
 
1591
+ // Collect inline unions from types, resolvers, and payloads
1592
+ const inlineUnionsFromTypes = collectInlineUnionsFromTypes({
1593
+ extractedTypes: input.extractedTypes,
1594
+ knownTypeNames: input.knownTypeNames,
1595
+ });
1596
+ const inlineUnionsFromResolvers = collectInlineUnionsFromResolvers({
1597
+ resolversResult: input.resolversResult,
1598
+ knownTypeNames: input.knownTypeNames,
1599
+ });
1600
+ const inlineUnionsFromPayloads = collectInlineUnionsFromPayloads({
1601
+ resolversResult: input.resolversResult,
1602
+ knownTypeNames: input.knownTypeNames,
1603
+ });
1604
+ const allInlineUnions = [
1605
+ ...inlineUnionsFromTypes,
1606
+ ...inlineUnionsFromResolvers,
1607
+ ...inlineUnionsFromPayloads,
1608
+ ];
1609
+
1610
+ // Build union type names map before generating auto types
1611
+ const unionTypeNames = buildUnionTypeNamesMap(allInlineUnions);
1612
+
883
1613
  const autoGeneratedTypes: AutoGeneratedType[] = [];
884
1614
  const diagnostics: Diagnostic[] = [];
885
1615
 
@@ -888,6 +1618,7 @@ export function generateAutoTypes(
888
1618
  inlineObj,
889
1619
  generatedTypeNames,
890
1620
  enumTypeNames,
1621
+ unionTypeNames,
891
1622
  });
892
1623
  autoGeneratedTypes.push(result.type);
893
1624
  diagnostics.push(...result.diagnostics);
@@ -901,9 +1632,23 @@ export function generateAutoTypes(
901
1632
  diagnostics.push(...result.diagnostics);
902
1633
  }
903
1634
 
1635
+ // Process inline unions
1636
+ const { types: unionTypes, diagnostics: unionDiagnostics } =
1637
+ processInlineUnions({
1638
+ inlineUnions: allInlineUnions,
1639
+ knownTypeNames: input.knownTypeNames,
1640
+ generatedTypeNames,
1641
+ enumTypeNames,
1642
+ unionTypeNames,
1643
+ extractedTypes: input.extractedTypes,
1644
+ });
1645
+ autoGeneratedTypes.push(...unionTypes);
1646
+ diagnostics.push(...unionDiagnostics);
1647
+
904
1648
  const updateParams: UpdateTypeNamesParams = {
905
1649
  generatedTypeNames,
906
1650
  enumTypeNames,
1651
+ unionTypeNames,
907
1652
  };
908
1653
 
909
1654
  const updatedExtractedTypes = updateExtractedTypes(