@constructive-io/graphql-codegen 3.2.1 → 3.3.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 (215) hide show
  1. package/cli/index.js +36 -43
  2. package/cli/shared.d.ts +15 -19
  3. package/cli/shared.js +104 -25
  4. package/client/error.js +31 -9
  5. package/client/execute.js +2 -2
  6. package/client/index.d.ts +3 -3
  7. package/client/index.js +6 -6
  8. package/core/ast.d.ts +1 -1
  9. package/core/ast.js +1 -1
  10. package/core/codegen/babel-ast.d.ts +1 -1
  11. package/core/codegen/babel-ast.js +2 -2
  12. package/core/codegen/barrel.d.ts +0 -6
  13. package/core/codegen/barrel.js +22 -19
  14. package/core/codegen/client.d.ts +2 -12
  15. package/core/codegen/client.js +7 -21
  16. package/core/codegen/custom-mutations.d.ts +0 -14
  17. package/core/codegen/custom-mutations.js +139 -88
  18. package/core/codegen/custom-queries.d.ts +0 -14
  19. package/core/codegen/custom-queries.js +483 -193
  20. package/core/codegen/hooks-ast.d.ts +75 -0
  21. package/core/codegen/hooks-ast.js +522 -0
  22. package/core/codegen/index.d.ts +16 -18
  23. package/core/codegen/index.js +42 -88
  24. package/core/codegen/invalidation.d.ts +1 -7
  25. package/core/codegen/invalidation.js +50 -16
  26. package/core/codegen/mutation-keys.d.ts +1 -10
  27. package/core/codegen/mutation-keys.js +22 -8
  28. package/core/codegen/mutations.d.ts +0 -13
  29. package/core/codegen/mutations.js +301 -366
  30. package/core/codegen/orm/barrel.d.ts +0 -5
  31. package/core/codegen/orm/barrel.js +5 -0
  32. package/core/codegen/orm/client-generator.d.ts +0 -5
  33. package/core/codegen/orm/client-generator.js +7 -2
  34. package/core/codegen/orm/client.js +3 -1
  35. package/core/codegen/orm/custom-ops-generator.d.ts +0 -6
  36. package/core/codegen/orm/custom-ops-generator.js +104 -51
  37. package/core/codegen/orm/index.d.ts +4 -4
  38. package/core/codegen/orm/index.js +28 -15
  39. package/core/codegen/orm/input-types-generator.d.ts +1 -13
  40. package/core/codegen/orm/input-types-generator.js +85 -23
  41. package/core/codegen/orm/model-generator.d.ts +0 -5
  42. package/core/codegen/orm/model-generator.js +309 -131
  43. package/core/codegen/orm/select-types.d.ts +19 -14
  44. package/core/codegen/queries.d.ts +0 -8
  45. package/core/codegen/queries.js +360 -559
  46. package/core/codegen/query-keys.d.ts +1 -1
  47. package/core/codegen/query-keys.js +37 -23
  48. package/core/codegen/scalars.js +3 -1
  49. package/core/codegen/schema-types-generator.d.ts +1 -1
  50. package/core/codegen/schema-types-generator.js +17 -2
  51. package/core/codegen/select-helpers.d.ts +19 -0
  52. package/core/codegen/select-helpers.js +40 -0
  53. package/core/codegen/selection.d.ts +4 -0
  54. package/core/codegen/selection.js +65 -0
  55. package/core/codegen/shared/index.d.ts +2 -15
  56. package/core/codegen/shared/index.js +17 -4
  57. package/core/codegen/templates/hooks-client.ts +49 -0
  58. package/core/codegen/templates/hooks-selection.ts +58 -0
  59. package/core/codegen/templates/orm-client.ts +8 -6
  60. package/core/codegen/templates/query-builder.ts +250 -46
  61. package/core/codegen/templates/select-types.ts +31 -14
  62. package/core/codegen/type-resolver.d.ts +1 -5
  63. package/core/codegen/type-resolver.js +0 -22
  64. package/core/codegen/types.d.ts +0 -3
  65. package/core/codegen/types.js +71 -14
  66. package/core/codegen/utils.d.ts +1 -4
  67. package/core/codegen/utils.js +4 -1
  68. package/core/config/index.d.ts +1 -1
  69. package/core/config/resolver.js +1 -3
  70. package/core/generate.js +38 -50
  71. package/core/index.d.ts +3 -3
  72. package/core/index.js +3 -4
  73. package/core/introspect/index.d.ts +6 -6
  74. package/core/introspect/index.js +5 -8
  75. package/core/introspect/infer-tables.d.ts +0 -14
  76. package/core/introspect/infer-tables.js +15 -1
  77. package/core/introspect/source/database.js +1 -1
  78. package/core/introspect/source/endpoint.d.ts +0 -6
  79. package/core/introspect/source/endpoint.js +7 -1
  80. package/core/introspect/source/index.d.ts +4 -4
  81. package/core/introspect/source/index.js +5 -9
  82. package/core/introspect/source/pgpm-module.js +3 -3
  83. package/core/introspect/transform-schema.d.ts +2 -2
  84. package/core/introspect/transform-schema.js +2 -2
  85. package/core/output/index.d.ts +1 -1
  86. package/core/output/index.js +2 -2
  87. package/core/output/writer.d.ts +3 -0
  88. package/core/output/writer.js +20 -1
  89. package/core/pipeline/index.d.ts +2 -2
  90. package/core/query-builder.d.ts +2 -2
  91. package/core/query-builder.js +1 -1
  92. package/core/watch/index.d.ts +4 -4
  93. package/core/watch/index.js +9 -9
  94. package/core/watch/orchestrator.js +5 -3
  95. package/esm/cli/index.js +37 -44
  96. package/esm/cli/shared.d.ts +15 -19
  97. package/esm/cli/shared.js +94 -23
  98. package/esm/client/error.js +31 -9
  99. package/esm/client/execute.js +2 -2
  100. package/esm/client/index.d.ts +3 -3
  101. package/esm/client/index.js +3 -3
  102. package/esm/core/ast.d.ts +1 -1
  103. package/esm/core/ast.js +1 -1
  104. package/esm/core/codegen/babel-ast.d.ts +1 -1
  105. package/esm/core/codegen/babel-ast.js +2 -2
  106. package/esm/core/codegen/barrel.d.ts +0 -6
  107. package/esm/core/codegen/barrel.js +23 -20
  108. package/esm/core/codegen/client.d.ts +2 -12
  109. package/esm/core/codegen/client.js +7 -21
  110. package/esm/core/codegen/custom-mutations.d.ts +0 -14
  111. package/esm/core/codegen/custom-mutations.js +141 -90
  112. package/esm/core/codegen/custom-queries.d.ts +0 -14
  113. package/esm/core/codegen/custom-queries.js +486 -196
  114. package/esm/core/codegen/hooks-ast.d.ts +75 -0
  115. package/esm/core/codegen/hooks-ast.js +424 -0
  116. package/esm/core/codegen/index.d.ts +16 -18
  117. package/esm/core/codegen/index.js +26 -71
  118. package/esm/core/codegen/invalidation.d.ts +1 -7
  119. package/esm/core/codegen/invalidation.js +51 -17
  120. package/esm/core/codegen/mutation-keys.d.ts +1 -10
  121. package/esm/core/codegen/mutation-keys.js +23 -9
  122. package/esm/core/codegen/mutations.d.ts +0 -13
  123. package/esm/core/codegen/mutations.js +302 -367
  124. package/esm/core/codegen/orm/barrel.d.ts +0 -5
  125. package/esm/core/codegen/orm/barrel.js +6 -1
  126. package/esm/core/codegen/orm/client-generator.d.ts +0 -5
  127. package/esm/core/codegen/orm/client-generator.js +7 -2
  128. package/esm/core/codegen/orm/client.js +3 -1
  129. package/esm/core/codegen/orm/custom-ops-generator.d.ts +0 -6
  130. package/esm/core/codegen/orm/custom-ops-generator.js +103 -50
  131. package/esm/core/codegen/orm/index.d.ts +4 -4
  132. package/esm/core/codegen/orm/index.js +25 -12
  133. package/esm/core/codegen/orm/input-types-generator.d.ts +1 -13
  134. package/esm/core/codegen/orm/input-types-generator.js +85 -23
  135. package/esm/core/codegen/orm/model-generator.d.ts +0 -5
  136. package/esm/core/codegen/orm/model-generator.js +310 -132
  137. package/esm/core/codegen/orm/select-types.d.ts +19 -14
  138. package/esm/core/codegen/queries.d.ts +0 -8
  139. package/esm/core/codegen/queries.js +362 -561
  140. package/esm/core/codegen/query-keys.d.ts +1 -1
  141. package/esm/core/codegen/query-keys.js +38 -24
  142. package/esm/core/codegen/scalars.js +3 -1
  143. package/esm/core/codegen/schema-types-generator.d.ts +1 -1
  144. package/esm/core/codegen/schema-types-generator.js +17 -2
  145. package/esm/core/codegen/select-helpers.d.ts +19 -0
  146. package/esm/core/codegen/select-helpers.js +35 -0
  147. package/esm/core/codegen/selection.d.ts +4 -0
  148. package/esm/core/codegen/selection.js +29 -0
  149. package/esm/core/codegen/shared/index.d.ts +2 -15
  150. package/esm/core/codegen/shared/index.js +16 -3
  151. package/esm/core/codegen/type-resolver.d.ts +1 -5
  152. package/esm/core/codegen/type-resolver.js +1 -22
  153. package/esm/core/codegen/types.d.ts +0 -3
  154. package/esm/core/codegen/types.js +72 -15
  155. package/esm/core/codegen/utils.d.ts +1 -4
  156. package/esm/core/codegen/utils.js +4 -1
  157. package/esm/core/config/index.d.ts +1 -1
  158. package/esm/core/config/resolver.js +2 -4
  159. package/esm/core/generate.js +38 -50
  160. package/esm/core/index.d.ts +3 -3
  161. package/esm/core/index.js +2 -3
  162. package/esm/core/introspect/index.d.ts +6 -6
  163. package/esm/core/introspect/index.js +3 -6
  164. package/esm/core/introspect/infer-tables.d.ts +0 -14
  165. package/esm/core/introspect/infer-tables.js +16 -2
  166. package/esm/core/introspect/source/database.js +2 -2
  167. package/esm/core/introspect/source/endpoint.d.ts +0 -6
  168. package/esm/core/introspect/source/endpoint.js +7 -1
  169. package/esm/core/introspect/source/index.d.ts +4 -4
  170. package/esm/core/introspect/source/index.js +6 -10
  171. package/esm/core/introspect/source/pgpm-module.js +3 -3
  172. package/esm/core/introspect/transform-schema.d.ts +2 -2
  173. package/esm/core/introspect/transform-schema.js +2 -2
  174. package/esm/core/output/index.d.ts +1 -1
  175. package/esm/core/output/index.js +1 -1
  176. package/esm/core/output/writer.d.ts +3 -0
  177. package/esm/core/output/writer.js +20 -1
  178. package/esm/core/pipeline/index.d.ts +2 -2
  179. package/esm/core/pipeline/index.js +2 -2
  180. package/esm/core/query-builder.d.ts +2 -2
  181. package/esm/core/query-builder.js +2 -2
  182. package/esm/core/watch/index.d.ts +4 -4
  183. package/esm/core/watch/index.js +3 -3
  184. package/esm/core/watch/orchestrator.js +5 -3
  185. package/esm/generators/index.d.ts +3 -3
  186. package/esm/generators/index.js +3 -3
  187. package/esm/generators/mutations.d.ts +1 -1
  188. package/esm/generators/select.d.ts +1 -1
  189. package/esm/index.d.ts +3 -3
  190. package/esm/index.js +1 -4
  191. package/esm/types/config.d.ts +0 -10
  192. package/esm/types/config.js +0 -2
  193. package/esm/types/index.d.ts +6 -6
  194. package/esm/types/index.js +1 -1
  195. package/generators/index.d.ts +3 -3
  196. package/generators/index.js +8 -8
  197. package/generators/mutations.d.ts +1 -1
  198. package/generators/select.d.ts +1 -1
  199. package/index.d.ts +3 -3
  200. package/index.js +11 -6
  201. package/package.json +10 -10
  202. package/types/config.d.ts +0 -10
  203. package/types/config.js +0 -2
  204. package/types/index.d.ts +6 -6
  205. package/types/index.js +2 -2
  206. package/core/codegen/gql-ast.d.ts +0 -41
  207. package/core/codegen/gql-ast.js +0 -353
  208. package/core/codegen/schema-gql-ast.d.ts +0 -51
  209. package/core/codegen/schema-gql-ast.js +0 -385
  210. package/core/codegen/templates/client.browser.ts +0 -271
  211. package/core/codegen/templates/client.node.ts +0 -337
  212. package/esm/core/codegen/gql-ast.d.ts +0 -41
  213. package/esm/core/codegen/gql-ast.js +0 -312
  214. package/esm/core/codegen/schema-gql-ast.d.ts +0 -51
  215. package/esm/core/codegen/schema-gql-ast.js +0 -343
@@ -1,8 +1,3 @@
1
- /**
2
- * Barrel file generators for ORM client (Babel AST-based)
3
- *
4
- * Generates index.ts files that re-export all models and operations.
5
- */
6
1
  import type { CleanTable } from '../../../types/schema';
7
2
  export interface GeneratedBarrelFile {
8
3
  fileName: string;
@@ -1,6 +1,11 @@
1
+ /**
2
+ * Barrel file generators for ORM client (Babel AST-based)
3
+ *
4
+ * Generates index.ts files that re-export all models and operations.
5
+ */
1
6
  import * as t from '@babel/types';
2
7
  import { generateCode } from '../babel-ast';
3
- import { getTableNames, lcFirst, getGeneratedFileHeader } from '../utils';
8
+ import { getGeneratedFileHeader, getTableNames, lcFirst } from '../utils';
4
9
  /**
5
10
  * Generate the models/index.ts barrel file
6
11
  */
@@ -1,8 +1,3 @@
1
- /**
2
- * ORM Client generator (Babel AST-based)
3
- *
4
- * Generates the createClient() factory function and main client file.
5
- */
6
1
  import type { CleanTable } from '../../../types/schema';
7
2
  export interface GeneratedClientFile {
8
3
  fileName: string;
@@ -1,8 +1,13 @@
1
+ /**
2
+ * ORM Client generator (Babel AST-based)
3
+ *
4
+ * Generates the createClient() factory function and main client file.
5
+ */
1
6
  import * as t from '@babel/types';
2
- import { generateCode, commentBlock } from '../babel-ast';
3
- import { getTableNames, lcFirst, getGeneratedFileHeader } from '../utils';
4
7
  import * as fs from 'fs';
5
8
  import * as path from 'path';
9
+ import { commentBlock, generateCode } from '../babel-ast';
10
+ import { getGeneratedFileHeader, getTableNames, lcFirst } from '../utils';
6
11
  /**
7
12
  * Find a template file path.
8
13
  * Templates are at ../templates/ relative to this file in both src/ and dist/.
@@ -34,7 +34,9 @@ export class FetchAdapter {
34
34
  return {
35
35
  ok: false,
36
36
  data: null,
37
- errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }],
37
+ errors: [
38
+ { message: `HTTP ${response.status}: ${response.statusText}` },
39
+ ],
38
40
  };
39
41
  }
40
42
  const json = (await response.json());
@@ -1,9 +1,3 @@
1
- /**
2
- * Custom operations generator for ORM client (Babel AST-based)
3
- *
4
- * Generates db.query.* and db.mutation.* namespaces for non-table operations
5
- * like login, register, currentUser, etc.
6
- */
7
1
  import type { CleanOperation } from '../../../types/schema';
8
2
  export interface GeneratedCustomOpsFile {
9
3
  fileName: string;
@@ -1,8 +1,14 @@
1
+ /**
2
+ * Custom operations generator for ORM client (Babel AST-based)
3
+ *
4
+ * Generates db.query.* and db.mutation.* namespaces for non-table operations
5
+ * like login, register, currentUser, etc.
6
+ */
1
7
  import * as t from '@babel/types';
2
8
  import { generateCode } from '../babel-ast';
3
- import { ucFirst, getGeneratedFileHeader } from '../utils';
4
- import { typeRefToTsType, isTypeRequired, getTypeBaseName, } from '../type-resolver';
5
- import { SCALAR_NAMES } from '../scalars';
9
+ import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers';
10
+ import { getTypeBaseName, isTypeRequired, typeRefToTsType, } from '../type-resolver';
11
+ import { getGeneratedFileHeader, ucFirst } from '../utils';
6
12
  /**
7
13
  * Collect all input type names used by operations
8
14
  * Includes Input, Filter, OrderBy, and Condition types
@@ -23,12 +29,6 @@ function collectInputTypeNamesFromOps(operations) {
23
29
  }
24
30
  return Array.from(inputTypes);
25
31
  }
26
- // Types that don't need Select types
27
- const NON_SELECT_TYPES = new Set([
28
- ...SCALAR_NAMES,
29
- 'Query',
30
- 'Mutation',
31
- ]);
32
32
  /**
33
33
  * Collect all payload/return type names from operations (for Select types)
34
34
  * Filters out scalar types
@@ -39,8 +39,6 @@ function collectPayloadTypeNamesFromOps(operations) {
39
39
  const baseName = getTypeBaseName(op.returnType);
40
40
  if (baseName &&
41
41
  !baseName.endsWith('Connection') &&
42
- baseName !== 'Query' &&
43
- baseName !== 'Mutation' &&
44
42
  !NON_SELECT_TYPES.has(baseName)) {
45
43
  payloadTypes.add(baseName);
46
44
  }
@@ -48,19 +46,20 @@ function collectPayloadTypeNamesFromOps(operations) {
48
46
  return Array.from(payloadTypes);
49
47
  }
50
48
  /**
51
- * Get the Select type name for a return type
52
- * Returns null for scalar types, Connection types (no select needed)
49
+ * Collect Connection and other non-scalar return type names that need importing
50
+ * (for typing QueryBuilder results on scalar/Connection operations)
53
51
  */
54
- function getSelectTypeName(returnType) {
55
- const baseName = getTypeBaseName(returnType);
56
- if (baseName &&
57
- !NON_SELECT_TYPES.has(baseName) &&
58
- baseName !== 'Query' &&
59
- baseName !== 'Mutation' &&
60
- !baseName.endsWith('Connection')) {
61
- return `${baseName}Select`;
52
+ function collectRawReturnTypeNames(operations) {
53
+ const types = new Set();
54
+ for (const op of operations) {
55
+ const baseName = getTypeBaseName(op.returnType);
56
+ if (baseName &&
57
+ !NON_SELECT_TYPES.has(baseName) &&
58
+ baseName.endsWith('Connection')) {
59
+ types.add(baseName);
60
+ }
62
61
  }
63
- return null;
62
+ return Array.from(types);
64
63
  }
65
64
  function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
66
65
  const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
@@ -95,7 +94,9 @@ function parseTypeAnnotation(typeStr) {
95
94
  if (typeStr === 'unknown')
96
95
  return t.tsUnknownKeyword();
97
96
  if (typeStr.includes(' | ')) {
98
- const parts = typeStr.split(' | ').map((p) => parseTypeAnnotation(p.trim()));
97
+ const parts = typeStr
98
+ .split(' | ')
99
+ .map((p) => parseTypeAnnotation(p.trim()));
99
100
  return t.tsUnionType(parts);
100
101
  }
101
102
  if (typeStr.endsWith('[]')) {
@@ -103,6 +104,18 @@ function parseTypeAnnotation(typeStr) {
103
104
  }
104
105
  return t.tsTypeReference(t.identifier(typeStr));
105
106
  }
107
+ function buildSelectedResultTsType(typeRef, payloadTypeName) {
108
+ if (typeRef.kind === 'NON_NULL' && typeRef.ofType) {
109
+ return buildSelectedResultTsType(typeRef.ofType, payloadTypeName);
110
+ }
111
+ if (typeRef.kind === 'LIST' && typeRef.ofType) {
112
+ return t.tsArrayType(buildSelectedResultTsType(typeRef.ofType, payloadTypeName));
113
+ }
114
+ return t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
115
+ t.tsTypeReference(t.identifier(payloadTypeName)),
116
+ t.tsTypeReference(t.identifier('S')),
117
+ ]));
118
+ }
106
119
  function buildOperationMethod(op, operationType) {
107
120
  const hasArgs = op.args.length > 0;
108
121
  const varTypeName = `${ucFirst(op.name)}Variables`;
@@ -120,25 +133,25 @@ function buildOperationMethod(op, operationType) {
120
133
  params.push(argsParam);
121
134
  }
122
135
  const optionsParam = t.identifier('options');
123
- optionsParam.optional = true;
136
+ optionsParam.optional = !selectTypeName;
124
137
  if (selectTypeName) {
125
- // Use DeepExact<S, SelectType> to enforce strict field validation
126
- // This catches invalid fields even when mixed with valid ones
127
- optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([
128
- (() => {
129
- const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
130
- t.tsTypeReference(t.identifier('S')),
131
- t.tsTypeReference(t.identifier(selectTypeName)),
132
- ]))));
133
- prop.optional = true;
134
- return prop;
135
- })(),
138
+ const selectProp = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))));
139
+ selectProp.optional = false;
140
+ optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsIntersectionType([
141
+ t.tsTypeLiteral([selectProp]),
142
+ t.tsTypeReference(t.identifier('StrictSelect'), t.tsTypeParameterInstantiation([
143
+ t.tsTypeReference(t.identifier('S')),
144
+ t.tsTypeReference(t.identifier(selectTypeName)),
145
+ ])),
136
146
  ]));
137
147
  }
138
148
  else {
139
149
  optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([
140
150
  (() => {
141
- const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([t.tsStringKeyword(), t.tsUnknownKeyword()]))));
151
+ const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
152
+ t.tsStringKeyword(),
153
+ t.tsUnknownKeyword(),
154
+ ]))));
142
155
  prop.optional = true;
143
156
  return prop;
144
157
  })(),
@@ -146,6 +159,12 @@ function buildOperationMethod(op, operationType) {
146
159
  }
147
160
  params.push(optionsParam);
148
161
  // Build the QueryBuilder call
162
+ const selectExpr = selectTypeName
163
+ ? t.memberExpression(t.identifier('options'), t.identifier('select'))
164
+ : t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true);
165
+ const entityTypeExpr = selectTypeName && payloadTypeName
166
+ ? t.stringLiteral(payloadTypeName)
167
+ : t.identifier('undefined');
149
168
  const queryBuilderArgs = t.objectExpression([
150
169
  t.objectProperty(t.identifier('client'), t.identifier('client'), false, true),
151
170
  t.objectProperty(t.identifier('operation'), t.stringLiteral(operationType)),
@@ -155,23 +174,34 @@ function buildOperationMethod(op, operationType) {
155
174
  t.stringLiteral(operationType),
156
175
  t.stringLiteral(ucFirst(op.name)),
157
176
  t.stringLiteral(op.name),
158
- t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true),
177
+ selectExpr,
159
178
  hasArgs ? t.identifier('args') : t.identifier('undefined'),
160
179
  t.arrayExpression(varDefs.map((v) => t.objectExpression([
161
180
  t.objectProperty(t.identifier('name'), t.stringLiteral(v.name)),
162
181
  t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)),
163
182
  ]))),
183
+ t.identifier('connectionFieldsMap'),
184
+ entityTypeExpr,
164
185
  ])),
165
186
  ]);
166
- const newExpr = t.newExpression(t.identifier('QueryBuilder'), [queryBuilderArgs]);
167
- // Add type parameter if we have a select type
187
+ const newExpr = t.newExpression(t.identifier('QueryBuilder'), [
188
+ queryBuilderArgs,
189
+ ]);
190
+ // Add type parameter to QueryBuilder for typed .unwrap() results
168
191
  if (selectTypeName && payloadTypeName) {
192
+ // Select-based type: use InferSelectResult<PayloadType, S>
193
+ newExpr.typeParameters = t.tsTypeParameterInstantiation([
194
+ t.tsTypeLiteral([
195
+ t.tsPropertySignature(t.identifier(op.name), t.tsTypeAnnotation(buildSelectedResultTsType(op.returnType, payloadTypeName))),
196
+ ]),
197
+ ]);
198
+ }
199
+ else {
200
+ // Scalar/Connection type: use raw TS type directly
201
+ const rawTsType = typeRefToTsType(op.returnType);
169
202
  newExpr.typeParameters = t.tsTypeParameterInstantiation([
170
203
  t.tsTypeLiteral([
171
- t.tsPropertySignature(t.identifier(op.name), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([
172
- t.tsTypeReference(t.identifier(payloadTypeName)),
173
- t.tsTypeReference(t.identifier('S')),
174
- ])))),
204
+ t.tsPropertySignature(t.identifier(op.name), t.tsTypeAnnotation(parseTypeAnnotation(rawTsType))),
175
205
  ]),
176
206
  ]);
177
207
  }
@@ -179,7 +209,6 @@ function buildOperationMethod(op, operationType) {
179
209
  // Add type parameters to arrow function if we have a select type
180
210
  if (selectTypeName) {
181
211
  const typeParam = t.tsTypeParameter(t.tsTypeReference(t.identifier(selectTypeName)), null, 'S');
182
- typeParam.const = true;
183
212
  arrowFunc.typeParameters = t.tsTypeParameterDeclaration([typeParam]);
184
213
  }
185
214
  return t.objectProperty(t.identifier(op.name), arrowFunc);
@@ -193,14 +222,26 @@ export function generateCustomQueryOpsFile(operations) {
193
222
  const inputTypeNames = collectInputTypeNamesFromOps(operations);
194
223
  const payloadTypeNames = collectPayloadTypeNamesFromOps(operations);
195
224
  const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`);
196
- const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames])];
225
+ const rawReturnTypeNames = collectRawReturnTypeNames(operations);
226
+ const allTypeImports = [
227
+ ...new Set([
228
+ ...inputTypeNames,
229
+ ...payloadTypeNames,
230
+ ...selectTypeNames,
231
+ ...rawReturnTypeNames,
232
+ ]),
233
+ ];
197
234
  // Add imports
198
235
  statements.push(createImportDeclaration('../client', ['OrmClient']));
199
- statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument']));
200
- statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true));
236
+ statements.push(createImportDeclaration('../query-builder', [
237
+ 'QueryBuilder',
238
+ 'buildCustomDocument',
239
+ ]));
240
+ statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true));
201
241
  if (allTypeImports.length > 0) {
202
242
  statements.push(createImportDeclaration('../input-types', allTypeImports, true));
203
243
  }
244
+ statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap']));
204
245
  // Generate variable interfaces
205
246
  for (const op of operations) {
206
247
  const varInterface = createVariablesInterface(op);
@@ -231,14 +272,26 @@ export function generateCustomMutationOpsFile(operations) {
231
272
  const inputTypeNames = collectInputTypeNamesFromOps(operations);
232
273
  const payloadTypeNames = collectPayloadTypeNamesFromOps(operations);
233
274
  const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`);
234
- const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames])];
275
+ const rawReturnTypeNames = collectRawReturnTypeNames(operations);
276
+ const allTypeImports = [
277
+ ...new Set([
278
+ ...inputTypeNames,
279
+ ...payloadTypeNames,
280
+ ...selectTypeNames,
281
+ ...rawReturnTypeNames,
282
+ ]),
283
+ ];
235
284
  // Add imports
236
285
  statements.push(createImportDeclaration('../client', ['OrmClient']));
237
- statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument']));
238
- statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true));
286
+ statements.push(createImportDeclaration('../query-builder', [
287
+ 'QueryBuilder',
288
+ 'buildCustomDocument',
289
+ ]));
290
+ statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true));
239
291
  if (allTypeImports.length > 0) {
240
292
  statements.push(createImportDeclaration('../input-types', allTypeImports, true));
241
293
  }
294
+ statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap']));
242
295
  // Generate variable interfaces
243
296
  for (const op of operations) {
244
297
  const varInterface = createVariablesInterface(op);
@@ -4,8 +4,8 @@
4
4
  * Main entry point for ORM code generation. Coordinates all generators
5
5
  * and produces the complete ORM client output.
6
6
  */
7
- import type { CleanTable, CleanOperation, TypeRegistry } from '../../../types/schema';
8
7
  import type { GraphQLSDKConfigTarget } from '../../../types/config';
8
+ import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema';
9
9
  export interface GeneratedFile {
10
10
  path: string;
11
11
  content: string;
@@ -39,7 +39,7 @@ export interface GenerateOrmResult {
39
39
  * Generate all ORM client files
40
40
  */
41
41
  export declare function generateOrm(options: GenerateOrmOptions): GenerateOrmResult;
42
- export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
43
- export { generateModelFile, generateAllModelFiles } from './model-generator';
44
- export { generateCustomQueryOpsFile, generateCustomMutationOpsFile } from './custom-ops-generator';
45
42
  export { generateModelsBarrel, generateTypesBarrel } from './barrel';
43
+ export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
44
+ export { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
45
+ export { generateAllModelFiles, generateModelFile } from './model-generator';
@@ -1,8 +1,8 @@
1
- import { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, generateCreateClientFile, } from './client-generator';
2
- import { generateAllModelFiles } from './model-generator';
3
- import { generateCustomQueryOpsFile, generateCustomMutationOpsFile, } from './custom-ops-generator';
4
1
  import { generateModelsBarrel, generateTypesBarrel } from './barrel';
5
- import { generateInputTypesFile, collectInputTypeNames, collectPayloadTypeNames } from './input-types-generator';
2
+ import { generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
3
+ import { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
4
+ import { collectInputTypeNames, collectPayloadTypeNames, generateInputTypesFile, } from './input-types-generator';
5
+ import { generateAllModelFiles } from './model-generator';
6
6
  /**
7
7
  * Generate all ORM client files
8
8
  */
@@ -18,9 +18,15 @@ export function generateOrm(options) {
18
18
  const clientFile = generateOrmClientFile();
19
19
  files.push({ path: clientFile.fileName, content: clientFile.content });
20
20
  const queryBuilderFile = generateQueryBuilderFile();
21
- files.push({ path: queryBuilderFile.fileName, content: queryBuilderFile.content });
21
+ files.push({
22
+ path: queryBuilderFile.fileName,
23
+ content: queryBuilderFile.content,
24
+ });
22
25
  const selectTypesFile = generateSelectTypesFile();
23
- files.push({ path: selectTypesFile.fileName, content: selectTypesFile.content });
26
+ files.push({
27
+ path: selectTypesFile.fileName,
28
+ content: selectTypesFile.content,
29
+ });
24
30
  // 2. Generate model files
25
31
  const modelFiles = generateAllModelFiles(tables, useSharedTypes);
26
32
  for (const modelFile of modelFiles) {
@@ -34,7 +40,8 @@ export function generateOrm(options) {
34
40
  files.push({ path: modelsBarrel.fileName, content: modelsBarrel.content });
35
41
  // 4. Generate comprehensive input types (entities, filters, orderBy, CRUD inputs, custom inputs, payload types)
36
42
  // Always generate if we have tables or custom operations
37
- if (tables.length > 0 || (typeRegistry && (hasCustomQueries || hasCustomMutations))) {
43
+ if (tables.length > 0 ||
44
+ (typeRegistry && (hasCustomQueries || hasCustomMutations))) {
38
45
  const allOps = [
39
46
  ...(customOperations?.queries ?? []),
40
47
  ...(customOperations?.mutations ?? []),
@@ -59,7 +66,10 @@ export function generateOrm(options) {
59
66
  }
60
67
  }
61
68
  const inputTypesFile = generateInputTypesFile(typeRegistry ?? new Map(), usedInputTypes, tables, usedPayloadTypes);
62
- files.push({ path: inputTypesFile.fileName, content: inputTypesFile.content });
69
+ files.push({
70
+ path: inputTypesFile.fileName,
71
+ content: inputTypesFile.content,
72
+ });
63
73
  }
64
74
  // 5. Generate custom operations (if any)
65
75
  if (hasCustomQueries && customOperations?.queries) {
@@ -68,7 +78,10 @@ export function generateOrm(options) {
68
78
  }
69
79
  if (hasCustomMutations && customOperations?.mutations) {
70
80
  const mutationOpsFile = generateCustomMutationOpsFile(customOperations.mutations);
71
- files.push({ path: mutationOpsFile.fileName, content: mutationOpsFile.content });
81
+ files.push({
82
+ path: mutationOpsFile.fileName,
83
+ content: mutationOpsFile.content,
84
+ });
72
85
  }
73
86
  // 6. Generate types barrel
74
87
  const typesBarrel = generateTypesBarrel(useSharedTypes);
@@ -87,7 +100,7 @@ export function generateOrm(options) {
87
100
  };
88
101
  }
89
102
  // Re-export generators for direct use
90
- export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
91
- export { generateModelFile, generateAllModelFiles } from './model-generator';
92
- export { generateCustomQueryOpsFile, generateCustomMutationOpsFile } from './custom-ops-generator';
93
103
  export { generateModelsBarrel, generateTypesBarrel } from './barrel';
104
+ export { generateOrmClientFile, generateQueryBuilderFile, generateSelectTypesFile, } from './client-generator';
105
+ export { generateCustomMutationOpsFile, generateCustomQueryOpsFile, } from './custom-ops-generator';
106
+ export { generateAllModelFiles, generateModelFile } from './model-generator';
@@ -1,16 +1,4 @@
1
- /**
2
- * Input types generator for ORM client (Babel AST-based)
3
- *
4
- * Generates TypeScript interfaces for:
5
- * 1. Scalar filter types (StringFilter, IntFilter, UUIDFilter, etc.)
6
- * 2. Entity interfaces (User, Order, etc.)
7
- * 3. Table filter types (UserFilter, OrderFilter, etc.)
8
- * 4. OrderBy enums (UsersOrderBy, OrdersOrderBy, etc.)
9
- * 5. Input types (LoginInput, CreateUserInput, etc.)
10
- *
11
- * Uses Babel AST for robust code generation.
12
- */
13
- import type { TypeRegistry, CleanArgument, CleanTable } from '../../../types/schema';
1
+ import type { CleanArgument, CleanTable, TypeRegistry } from '../../../types/schema';
14
2
  export interface GeneratedInputTypesFile {
15
3
  fileName: string;
16
4
  content: string;
@@ -1,9 +1,21 @@
1
+ /**
2
+ * Input types generator for ORM client (Babel AST-based)
3
+ *
4
+ * Generates TypeScript interfaces for:
5
+ * 1. Scalar filter types (StringFilter, IntFilter, UUIDFilter, etc.)
6
+ * 2. Entity interfaces (User, Order, etc.)
7
+ * 3. Table filter types (UserFilter, OrderFilter, etc.)
8
+ * 4. OrderBy enums (UsersOrderBy, OrdersOrderBy, etc.)
9
+ * 5. Input types (LoginInput, CreateUserInput, etc.)
10
+ *
11
+ * Uses Babel AST for robust code generation.
12
+ */
1
13
  import * as t from '@babel/types';
2
- import { generateCode, addLineComment } from '../babel-ast';
3
- import { getTableNames, getFilterTypeName, getConditionTypeName, getOrderByTypeName, isRelationField, getGeneratedFileHeader, } from '../utils';
4
14
  import { pluralize } from 'inflekt';
15
+ import { addLineComment, generateCode } from '../babel-ast';
16
+ import { scalarToFilterType, scalarToTsType } from '../scalars';
5
17
  import { getTypeBaseName } from '../type-resolver';
6
- import { scalarToTsType, scalarToFilterType } from '../scalars';
18
+ import { getConditionTypeName, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, isRelationField, } from '../utils';
7
19
  // ============================================================================
8
20
  // Constants
9
21
  // ============================================================================
@@ -80,7 +92,9 @@ function parseTypeString(typeStr) {
80
92
  const match = typeStr.match(/^([^<]+)<(.+)>$/);
81
93
  if (match) {
82
94
  const [, baseName, params] = match;
83
- const typeParams = params.split(',').map((p) => parseTypeString(p.trim()));
95
+ const typeParams = params
96
+ .split(',')
97
+ .map((p) => parseTypeString(p.trim()));
84
98
  return t.tsTypeReference(t.identifier(baseName), t.tsTypeParameterInstantiation(typeParams));
85
99
  }
86
100
  }
@@ -364,9 +378,7 @@ function generateRelationHelperTypes() {
364
378
  createPropertySignature('pageInfo', 'PageInfo', false),
365
379
  ];
366
380
  const connectionResultBody = t.tsInterfaceBody(connectionResultProps);
367
- const connectionResultDecl = t.tsInterfaceDeclaration(t.identifier('ConnectionResult'), t.tsTypeParameterDeclaration([
368
- t.tsTypeParameter(null, null, 'T'),
369
- ]), null, connectionResultBody);
381
+ const connectionResultDecl = t.tsInterfaceDeclaration(t.identifier('ConnectionResult'), t.tsTypeParameterDeclaration([t.tsTypeParameter(null, null, 'T')]), null, connectionResultBody);
370
382
  statements.push(t.exportNamedDeclaration(connectionResultDecl));
371
383
  // PageInfo interface
372
384
  statements.push(createExportedInterface('PageInfo', [
@@ -801,6 +813,10 @@ function generateCrudInputTypes(table) {
801
813
  const statements = [];
802
814
  const { typeName } = getTableNames(table);
803
815
  const patchName = `${typeName}Patch`;
816
+ const pkFields = getPrimaryKeyInfo(table);
817
+ const pkField = pkFields[0];
818
+ const pkFieldName = pkField?.name ?? 'id';
819
+ const pkFieldTsType = pkField?.tsType ?? 'string';
804
820
  // Create input
805
821
  statements.push(buildCreateInputInterface(table));
806
822
  // Patch interface
@@ -808,13 +824,13 @@ function generateCrudInputTypes(table) {
808
824
  // Update input
809
825
  statements.push(createExportedInterface(`Update${typeName}Input`, [
810
826
  { name: 'clientMutationId', type: 'string', optional: true },
811
- { name: 'id', type: 'string', optional: false },
827
+ { name: pkFieldName, type: pkFieldTsType, optional: false },
812
828
  { name: 'patch', type: patchName, optional: false },
813
829
  ]));
814
830
  // Delete input
815
831
  statements.push(createExportedInterface(`Delete${typeName}Input`, [
816
832
  { name: 'clientMutationId', type: 'string', optional: true },
817
- { name: 'id', type: 'string', optional: false },
833
+ { name: pkFieldName, type: pkFieldTsType, optional: false },
818
834
  ]));
819
835
  return statements;
820
836
  }
@@ -949,8 +965,7 @@ export function collectPayloadTypeNames(operations) {
949
965
  const payloadTypes = new Set();
950
966
  for (const op of operations) {
951
967
  const baseName = getTypeBaseName(op.returnType);
952
- if (baseName &&
953
- (baseName.endsWith('Payload') || !baseName.endsWith('Connection'))) {
968
+ if (baseName) {
954
969
  payloadTypes.add(baseName);
955
970
  }
956
971
  }
@@ -1055,6 +1070,48 @@ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTy
1055
1070
  return statements;
1056
1071
  }
1057
1072
  // ============================================================================
1073
+ // Connection Fields Map Generator
1074
+ // ============================================================================
1075
+ /**
1076
+ * Generate a runtime map of entity type → { fieldName: relatedTypeName }
1077
+ * for all hasMany and manyToMany relations. Used by buildSelections()
1078
+ * to detect connection fields that need `nodes { ... }` wrapping.
1079
+ */
1080
+ function generateConnectionFieldsMap(tables, tableByName) {
1081
+ const properties = [];
1082
+ for (const table of tables) {
1083
+ const { typeName } = getTableNames(table);
1084
+ const fieldEntries = [];
1085
+ for (const relation of table.relations.hasMany) {
1086
+ if (!relation.fieldName)
1087
+ continue;
1088
+ const relatedTypeName = getRelatedTypeName(relation.referencedByTable, tableByName);
1089
+ fieldEntries.push(t.objectProperty(t.stringLiteral(relation.fieldName), t.stringLiteral(relatedTypeName)));
1090
+ }
1091
+ for (const relation of table.relations.manyToMany) {
1092
+ if (!relation.fieldName)
1093
+ continue;
1094
+ const relatedTypeName = getRelatedTypeName(relation.rightTable, tableByName);
1095
+ fieldEntries.push(t.objectProperty(t.stringLiteral(relation.fieldName), t.stringLiteral(relatedTypeName)));
1096
+ }
1097
+ if (fieldEntries.length > 0) {
1098
+ properties.push(t.objectProperty(t.stringLiteral(typeName), t.objectExpression(fieldEntries)));
1099
+ }
1100
+ }
1101
+ const decl = t.variableDeclaration('const', [
1102
+ t.variableDeclarator(t.identifier('connectionFieldsMap'), t.tsAsExpression(t.objectExpression(properties), t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
1103
+ t.tsStringKeyword(),
1104
+ t.tsTypeReference(t.identifier('Record'), t.tsTypeParameterInstantiation([
1105
+ t.tsStringKeyword(),
1106
+ t.tsStringKeyword(),
1107
+ ])),
1108
+ ])))),
1109
+ ]);
1110
+ const statements = [t.exportNamedDeclaration(decl)];
1111
+ addSectionComment(statements, 'Connection Fields Map');
1112
+ return statements;
1113
+ }
1114
+ // ============================================================================
1058
1115
  // Main Generator (AST-based)
1059
1116
  // ============================================================================
1060
1117
  /**
@@ -1062,30 +1119,35 @@ function generatePayloadTypes(typeRegistry, usedPayloadTypes, alreadyGeneratedTy
1062
1119
  */
1063
1120
  export function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloadTypes) {
1064
1121
  const statements = [];
1122
+ const tablesList = tables ?? [];
1123
+ const hasTables = tablesList.length > 0;
1124
+ const tableByName = new Map(tablesList.map((table) => [table.name, table]));
1065
1125
  // 1. Scalar filter types
1066
1126
  statements.push(...generateScalarFilterTypes());
1067
1127
  // 2. Enum types used by table fields
1068
- if (tables && tables.length > 0) {
1069
- const enumTypes = collectEnumTypesFromTables(tables, typeRegistry);
1128
+ if (hasTables) {
1129
+ const enumTypes = collectEnumTypesFromTables(tablesList, typeRegistry);
1070
1130
  statements.push(...generateEnumTypes(typeRegistry, enumTypes));
1071
1131
  }
1072
1132
  // 3. Entity and relation types (if tables provided)
1073
- if (tables && tables.length > 0) {
1074
- const tableByName = new Map(tables.map((table) => [table.name, table]));
1075
- statements.push(...generateEntityTypes(tables));
1133
+ if (hasTables) {
1134
+ statements.push(...generateEntityTypes(tablesList));
1076
1135
  statements.push(...generateRelationHelperTypes());
1077
- statements.push(...generateEntityRelationTypes(tables, tableByName));
1078
- statements.push(...generateEntityWithRelations(tables));
1079
- statements.push(...generateEntitySelectTypes(tables, tableByName));
1136
+ statements.push(...generateEntityRelationTypes(tablesList, tableByName));
1137
+ statements.push(...generateEntityWithRelations(tablesList));
1138
+ statements.push(...generateEntitySelectTypes(tablesList, tableByName));
1080
1139
  // 4. Table filter types
1081
- statements.push(...generateTableFilterTypes(tables));
1140
+ statements.push(...generateTableFilterTypes(tablesList));
1082
1141
  // 4b. Table condition types (simple equality filter)
1083
- statements.push(...generateTableConditionTypes(tables));
1142
+ statements.push(...generateTableConditionTypes(tablesList));
1084
1143
  // 5. OrderBy types
1085
- statements.push(...generateOrderByTypes(tables));
1144
+ statements.push(...generateOrderByTypes(tablesList));
1086
1145
  // 6. CRUD input types
1087
- statements.push(...generateAllCrudInputTypes(tables));
1146
+ statements.push(...generateAllCrudInputTypes(tablesList));
1088
1147
  }
1148
+ // 6b. Connection fields map (runtime metadata for buildSelections)
1149
+ // Always emit this export so generated model/custom-op imports stay valid.
1150
+ statements.push(...generateConnectionFieldsMap(tablesList, tableByName));
1089
1151
  // 7. Custom input types from TypeRegistry
1090
1152
  const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined;
1091
1153
  statements.push(...generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes));
@@ -1,8 +1,3 @@
1
- /**
2
- * Model class generator for ORM client (Babel AST-based)
3
- *
4
- * Generates per-table model classes with findMany, findFirst, create, update, delete methods.
5
- */
6
1
  import type { CleanTable } from '../../../types/schema';
7
2
  export interface GeneratedModelFile {
8
3
  fileName: string;