@constructive-io/graphql-codegen 2.24.0 → 2.24.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.
@@ -72,6 +72,7 @@ function generateListQueryHook(table, options = {}) {
72
72
  const hookName = (0, utils_1.getListQueryHookName)(table);
73
73
  const queryName = (0, utils_1.getAllRowsQueryName)(table);
74
74
  const filterTypeName = (0, utils_1.getFilterTypeName)(table);
75
+ const conditionTypeName = (0, utils_1.getConditionTypeName)(table);
75
76
  const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
76
77
  const scalarFields = (0, utils_1.getScalarFields)(table);
77
78
  const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
@@ -136,6 +137,59 @@ function generateListQueryHook(table, options = {}) {
136
137
  })
137
138
  .filter((f) => f !== null);
138
139
  statements.push(createFilterInterfaceDeclaration(filterTypeName, fieldFilters, false));
140
+ // Generate Condition interface (simple equality filter with scalar types)
141
+ // Track non-primitive types (enums) that need to be imported
142
+ const enumTypesUsed = new Set();
143
+ const conditionProperties = scalarFields.map((field) => {
144
+ const tsType = (0, utils_1.fieldTypeToTs)(field.type);
145
+ const isPrimitive = tsType === 'string' ||
146
+ tsType === 'number' ||
147
+ tsType === 'boolean' ||
148
+ tsType === 'unknown' ||
149
+ tsType.endsWith('[]');
150
+ let typeAnnotation;
151
+ if (field.type.isArray) {
152
+ const baseType = tsType.replace('[]', '');
153
+ const isBasePrimitive = baseType === 'string' ||
154
+ baseType === 'number' ||
155
+ baseType === 'boolean' ||
156
+ baseType === 'unknown';
157
+ if (!isBasePrimitive) {
158
+ enumTypesUsed.add(baseType);
159
+ }
160
+ typeAnnotation = t.tsArrayType(baseType === 'string'
161
+ ? t.tsStringKeyword()
162
+ : baseType === 'number'
163
+ ? t.tsNumberKeyword()
164
+ : baseType === 'boolean'
165
+ ? t.tsBooleanKeyword()
166
+ : t.tsTypeReference(t.identifier(baseType)));
167
+ }
168
+ else {
169
+ if (!isPrimitive) {
170
+ enumTypesUsed.add(tsType);
171
+ }
172
+ typeAnnotation =
173
+ tsType === 'string'
174
+ ? t.tsStringKeyword()
175
+ : tsType === 'number'
176
+ ? t.tsNumberKeyword()
177
+ : tsType === 'boolean'
178
+ ? t.tsBooleanKeyword()
179
+ : t.tsTypeReference(t.identifier(tsType));
180
+ }
181
+ const prop = t.tsPropertySignature(t.identifier(field.name), t.tsTypeAnnotation(typeAnnotation));
182
+ prop.optional = true;
183
+ return prop;
184
+ });
185
+ // Add import for enum types if any are used
186
+ if (enumTypesUsed.size > 0) {
187
+ const schemaTypesImport = t.importDeclaration(Array.from(enumTypesUsed).map((et) => t.importSpecifier(t.identifier(et), t.identifier(et))), t.stringLiteral('../schema-types'));
188
+ schemaTypesImport.importKind = 'type';
189
+ statements.push(schemaTypesImport);
190
+ }
191
+ const conditionInterface = t.tsInterfaceDeclaration(t.identifier(conditionTypeName), null, null, t.tsInterfaceBody(conditionProperties));
192
+ statements.push(conditionInterface);
139
193
  const orderByValues = [
140
194
  ...scalarFields.flatMap((f) => [
141
195
  `${(0, utils_1.toScreamingSnake)(f.name)}_ASC`,
@@ -153,16 +207,36 @@ function generateListQueryHook(table, options = {}) {
153
207
  p.optional = true;
154
208
  return p;
155
209
  })(),
210
+ (() => {
211
+ const p = t.tsPropertySignature(t.identifier('last'), t.tsTypeAnnotation(t.tsNumberKeyword()));
212
+ p.optional = true;
213
+ return p;
214
+ })(),
156
215
  (() => {
157
216
  const p = t.tsPropertySignature(t.identifier('offset'), t.tsTypeAnnotation(t.tsNumberKeyword()));
158
217
  p.optional = true;
159
218
  return p;
160
219
  })(),
220
+ (() => {
221
+ const p = t.tsPropertySignature(t.identifier('before'), t.tsTypeAnnotation(t.tsStringKeyword()));
222
+ p.optional = true;
223
+ return p;
224
+ })(),
225
+ (() => {
226
+ const p = t.tsPropertySignature(t.identifier('after'), t.tsTypeAnnotation(t.tsStringKeyword()));
227
+ p.optional = true;
228
+ return p;
229
+ })(),
161
230
  (() => {
162
231
  const p = t.tsPropertySignature(t.identifier('filter'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterTypeName))));
163
232
  p.optional = true;
164
233
  return p;
165
234
  })(),
235
+ (() => {
236
+ const p = t.tsPropertySignature(t.identifier('condition'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(conditionTypeName))));
237
+ p.optional = true;
238
+ return p;
239
+ })(),
166
240
  (() => {
167
241
  const p = t.tsPropertySignature(t.identifier('orderBy'), t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(orderByTypeName)))));
168
242
  p.optional = true;
@@ -222,9 +296,9 @@ function generateListQueryHook(table, options = {}) {
222
296
  hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [
223
297
  t.objectExpression([
224
298
  t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('list')), [t.identifier('variables'), t.identifier('scope')])),
225
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
226
- t.identifier(`${queryName}QueryDocument`),
227
- t.identifier('variables'),
299
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
300
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryResult`)),
301
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryVariables`)),
228
302
  ]))),
229
303
  t.spreadElement(t.identifier('queryOptions')),
230
304
  ]),
@@ -234,9 +308,9 @@ function generateListQueryHook(table, options = {}) {
234
308
  hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [
235
309
  t.objectExpression([
236
310
  t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('list')), [t.identifier('variables')])),
237
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
238
- t.identifier(`${queryName}QueryDocument`),
239
- t.identifier('variables'),
311
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
312
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryResult`)),
313
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryVariables`)),
240
314
  ]))),
241
315
  t.spreadElement(t.identifier('options')),
242
316
  ]),
@@ -248,9 +322,9 @@ function generateListQueryHook(table, options = {}) {
248
322
  t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(`${queryName}QueryKey`), [
249
323
  t.identifier('variables'),
250
324
  ])),
251
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
252
- t.identifier(`${queryName}QueryDocument`),
253
- t.identifier('variables'),
325
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
326
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryResult`)),
327
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryVariables`)),
254
328
  ]))),
255
329
  t.spreadElement(t.identifier('options')),
256
330
  ]),
@@ -298,10 +372,9 @@ function generateListQueryHook(table, options = {}) {
298
372
  statements.push(hookExport);
299
373
  }
300
374
  const fetchFuncBody = t.blockStatement([
301
- t.returnStatement(t.callExpression(t.identifier('execute'), [
302
- t.identifier(`${queryName}QueryDocument`),
303
- t.identifier('variables'),
304
- t.identifier('options'),
375
+ t.returnStatement((0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
376
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryResult`)),
377
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryVariables`)),
305
378
  ])),
306
379
  ]);
307
380
  const fetchFunc = t.functionDeclaration(t.identifier(`fetch${(0, utils_1.ucFirst)(pluralName)}Query`), [
@@ -352,10 +425,9 @@ function generateListQueryHook(table, options = {}) {
352
425
  t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [
353
426
  t.objectExpression([
354
427
  t.objectProperty(t.identifier('queryKey'), prefetchQueryKeyExpr),
355
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
356
- t.identifier(`${queryName}QueryDocument`),
357
- t.identifier('variables'),
358
- t.identifier('options'),
428
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
429
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryResult`)),
430
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(pluralName)}QueryVariables`)),
359
431
  ]))),
360
432
  ]),
361
433
  ]))),
@@ -385,6 +457,10 @@ function generateListQueryHook(table, options = {}) {
385
457
  };
386
458
  }
387
459
  function generateSingleQueryHook(table, options = {}) {
460
+ // Skip tables with composite keys - they are handled as custom queries
461
+ if (!(0, utils_1.hasValidPrimaryKey)(table)) {
462
+ return null;
463
+ }
388
464
  const { reactQueryEnabled = true, useCentralizedKeys = true, hasRelationships = false, } = options;
389
465
  const { typeName, singularName } = (0, utils_1.getTableNames)(table);
390
466
  const hookName = (0, utils_1.getSingleQueryHookName)(table);
@@ -492,9 +568,9 @@ function generateSingleQueryHook(table, options = {}) {
492
568
  t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
493
569
  t.identifier('scope'),
494
570
  ])),
495
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
496
- t.identifier(`${queryName}QueryDocument`),
497
- t.identifier('variables'),
571
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
572
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryResult`)),
573
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`)),
498
574
  ]))),
499
575
  t.spreadElement(t.identifier('queryOptions')),
500
576
  ]),
@@ -506,9 +582,9 @@ function generateSingleQueryHook(table, options = {}) {
506
582
  t.objectProperty(t.identifier('queryKey'), t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [
507
583
  t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
508
584
  ])),
509
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
510
- t.identifier(`${queryName}QueryDocument`),
511
- t.identifier('variables'),
585
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
586
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryResult`)),
587
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`)),
512
588
  ]))),
513
589
  t.spreadElement(t.identifier('options')),
514
590
  ]),
@@ -520,9 +596,9 @@ function generateSingleQueryHook(table, options = {}) {
520
596
  t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(`${queryName}QueryKey`), [
521
597
  t.memberExpression(t.identifier('variables'), t.identifier(pkName)),
522
598
  ])),
523
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
524
- t.identifier(`${queryName}QueryDocument`),
525
- t.identifier('variables'),
599
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], [
600
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryResult`)),
601
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`)),
526
602
  ]))),
527
603
  t.spreadElement(t.identifier('options')),
528
604
  ]),
@@ -566,10 +642,9 @@ function generateSingleQueryHook(table, options = {}) {
566
642
  statements.push(hookExport);
567
643
  }
568
644
  const fetchFuncBody = t.blockStatement([
569
- t.returnStatement(t.callExpression(t.identifier('execute'), [
570
- t.identifier(`${queryName}QueryDocument`),
571
- t.identifier('variables'),
572
- t.identifier('options'),
645
+ t.returnStatement((0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
646
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryResult`)),
647
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`)),
573
648
  ])),
574
649
  ]);
575
650
  const fetchFunc = t.functionDeclaration(t.identifier(`fetch${(0, utils_1.ucFirst)(singularName)}Query`), [
@@ -616,10 +691,9 @@ function generateSingleQueryHook(table, options = {}) {
616
691
  t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [
617
692
  t.objectExpression([
618
693
  t.objectProperty(t.identifier('queryKey'), prefetchQueryKeyExpr),
619
- t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
620
- t.identifier(`${queryName}QueryDocument`),
621
- t.identifier('variables'),
622
- t.identifier('options'),
694
+ t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], (0, babel_ast_1.createTypedCallExpression)(t.identifier('execute'), [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], [
695
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryResult`)),
696
+ t.tsTypeReference(t.identifier(`${(0, utils_1.ucFirst)(singularName)}QueryVariables`)),
623
697
  ]))),
624
698
  ]),
625
699
  ]))),
@@ -652,7 +726,10 @@ function generateAllQueryHooks(tables, options = {}) {
652
726
  const files = [];
653
727
  for (const table of tables) {
654
728
  files.push(generateListQueryHook(table, options));
655
- files.push(generateSingleQueryHook(table, options));
729
+ const singleHook = generateSingleQueryHook(table, options);
730
+ if (singleHook) {
731
+ files.push(singleHook);
732
+ }
656
733
  }
657
734
  return files;
658
735
  }
@@ -171,6 +171,12 @@ export declare function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[];
171
171
  * Get primary key field names (convenience wrapper)
172
172
  */
173
173
  export declare function getPrimaryKeyFields(table: CleanTable): string[];
174
+ /**
175
+ * Check if table has a valid single-field primary key
176
+ * Used to determine if a single query hook can be generated
177
+ * Tables with composite keys return false (handled as custom queries)
178
+ */
179
+ export declare function hasValidPrimaryKey(table: CleanTable): boolean;
174
180
  /**
175
181
  * Generate query key prefix for a table
176
182
  * e.g., "cars" for list queries, "car" for detail queries
@@ -35,6 +35,7 @@ exports.isRelationField = isRelationField;
35
35
  exports.getScalarFields = getScalarFields;
36
36
  exports.getPrimaryKeyInfo = getPrimaryKeyInfo;
37
37
  exports.getPrimaryKeyFields = getPrimaryKeyFields;
38
+ exports.hasValidPrimaryKey = hasValidPrimaryKey;
38
39
  exports.getQueryKeyPrefix = getQueryKeyPrefix;
39
40
  exports.getGeneratedFileHeader = getGeneratedFileHeader;
40
41
  exports.indent = indent;
@@ -331,6 +332,24 @@ function getPrimaryKeyInfo(table) {
331
332
  function getPrimaryKeyFields(table) {
332
333
  return getPrimaryKeyInfo(table).map((pk) => pk.name);
333
334
  }
335
+ /**
336
+ * Check if table has a valid single-field primary key
337
+ * Used to determine if a single query hook can be generated
338
+ * Tables with composite keys return false (handled as custom queries)
339
+ */
340
+ function hasValidPrimaryKey(table) {
341
+ // Check for explicit primary key constraint with single field
342
+ const pk = table.constraints?.primaryKey?.[0];
343
+ if (pk && pk.fields.length === 1) {
344
+ return true;
345
+ }
346
+ // Check for 'id' field as fallback
347
+ const idField = table.fields.find((f) => f.name.toLowerCase() === 'id');
348
+ if (idField) {
349
+ return true;
350
+ }
351
+ return false;
352
+ }
334
353
  // ============================================================================
335
354
  // Query key generation
336
355
  // ============================================================================
@@ -44,3 +44,10 @@ export declare function typedParam(name: string, typeAnnotation: t.TSType, optio
44
44
  * Create keyof typeof expression - complex nested type operators
45
45
  */
46
46
  export declare function keyofTypeof(name: string): t.TSTypeOperator;
47
+ /**
48
+ * Create a call expression with TypeScript type parameters
49
+ *
50
+ * This is used to generate typed function calls like:
51
+ * execute<ResultType, VariablesType>(document, variables)
52
+ */
53
+ export declare function createTypedCallExpression(callee: t.Expression, args: (t.Expression | t.SpreadElement)[], typeParams: t.TSType[]): t.CallExpression;
@@ -95,3 +95,17 @@ export function keyofTypeof(name) {
95
95
  keyofOp.operator = 'keyof';
96
96
  return keyofOp;
97
97
  }
98
+ /**
99
+ * Create a call expression with TypeScript type parameters
100
+ *
101
+ * This is used to generate typed function calls like:
102
+ * execute<ResultType, VariablesType>(document, variables)
103
+ */
104
+ export function createTypedCallExpression(callee, args, typeParams) {
105
+ const call = t.callExpression(callee, args);
106
+ if (typeParams.length > 0) {
107
+ // @ts-ignore - Babel types support typeParameters on CallExpression for TS
108
+ call.typeParameters = t.tsTypeParameterInstantiation(typeParams);
109
+ }
110
+ return call;
111
+ }
@@ -1,6 +1,6 @@
1
1
  import * as t from '@babel/types';
2
2
  import { generateCode, addJSDocComment } from './babel-ast';
3
- import { getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, } from './utils';
3
+ import { getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, } from './utils';
4
4
  import { getOperationHookName } from './type-resolver';
5
5
  /**
6
6
  * Helper to create export * from './module' statement
@@ -16,9 +16,12 @@ export function generateQueriesBarrel(tables) {
16
16
  // Export all query hooks
17
17
  for (const table of tables) {
18
18
  const listHookName = getListQueryHookName(table);
19
- const singleHookName = getSingleQueryHookName(table);
20
19
  statements.push(exportAllFrom(`./${listHookName}`));
21
- statements.push(exportAllFrom(`./${singleHookName}`));
20
+ // Only export single query hook if table has valid primary key
21
+ if (hasValidPrimaryKey(table)) {
22
+ const singleHookName = getSingleQueryHookName(table);
23
+ statements.push(exportAllFrom(`./${singleHookName}`));
24
+ }
22
25
  }
23
26
  // Add file header as leading comment on first statement
24
27
  if (statements.length > 0) {
@@ -135,17 +138,30 @@ export function generateMainBarrel(tables, options = {}) {
135
138
  */
136
139
  export function generateCustomQueriesBarrel(tables, customQueryNames) {
137
140
  const statements = [];
141
+ const exportedHooks = new Set();
138
142
  // Export all table query hooks
139
143
  for (const table of tables) {
140
144
  const listHookName = getListQueryHookName(table);
141
- const singleHookName = getSingleQueryHookName(table);
142
- statements.push(exportAllFrom(`./${listHookName}`));
143
- statements.push(exportAllFrom(`./${singleHookName}`));
145
+ if (!exportedHooks.has(listHookName)) {
146
+ statements.push(exportAllFrom(`./${listHookName}`));
147
+ exportedHooks.add(listHookName);
148
+ }
149
+ // Only export single query hook if table has valid primary key
150
+ if (hasValidPrimaryKey(table)) {
151
+ const singleHookName = getSingleQueryHookName(table);
152
+ if (!exportedHooks.has(singleHookName)) {
153
+ statements.push(exportAllFrom(`./${singleHookName}`));
154
+ exportedHooks.add(singleHookName);
155
+ }
156
+ }
144
157
  }
145
- // Add custom query hooks
158
+ // Add custom query hooks (skip if already exported from table hooks)
146
159
  for (const name of customQueryNames) {
147
160
  const hookName = getOperationHookName(name, 'query');
148
- statements.push(exportAllFrom(`./${hookName}`));
161
+ if (!exportedHooks.has(hookName)) {
162
+ statements.push(exportAllFrom(`./${hookName}`));
163
+ exportedHooks.add(hookName);
164
+ }
149
165
  }
150
166
  // Add file header as leading comment on first statement
151
167
  if (statements.length > 0) {
@@ -162,24 +178,37 @@ export function generateCustomQueriesBarrel(tables, customQueryNames) {
162
178
  */
163
179
  export function generateCustomMutationsBarrel(tables, customMutationNames) {
164
180
  const statements = [];
181
+ const exportedHooks = new Set();
165
182
  // Export all table mutation hooks
166
183
  for (const table of tables) {
167
184
  const createHookName = getCreateMutationHookName(table);
168
- const updateHookName = getUpdateMutationHookName(table);
169
- const deleteHookName = getDeleteMutationHookName(table);
170
- statements.push(exportAllFrom(`./${createHookName}`));
185
+ if (!exportedHooks.has(createHookName)) {
186
+ statements.push(exportAllFrom(`./${createHookName}`));
187
+ exportedHooks.add(createHookName);
188
+ }
171
189
  // Only add update/delete if they exist
172
190
  if (table.query?.update !== null) {
173
- statements.push(exportAllFrom(`./${updateHookName}`));
191
+ const updateHookName = getUpdateMutationHookName(table);
192
+ if (!exportedHooks.has(updateHookName)) {
193
+ statements.push(exportAllFrom(`./${updateHookName}`));
194
+ exportedHooks.add(updateHookName);
195
+ }
174
196
  }
175
197
  if (table.query?.delete !== null) {
176
- statements.push(exportAllFrom(`./${deleteHookName}`));
198
+ const deleteHookName = getDeleteMutationHookName(table);
199
+ if (!exportedHooks.has(deleteHookName)) {
200
+ statements.push(exportAllFrom(`./${deleteHookName}`));
201
+ exportedHooks.add(deleteHookName);
202
+ }
177
203
  }
178
204
  }
179
- // Add custom mutation hooks
205
+ // Add custom mutation hooks (skip if already exported from table hooks)
180
206
  for (const name of customMutationNames) {
181
207
  const hookName = getOperationHookName(name, 'mutation');
182
- statements.push(exportAllFrom(`./${hookName}`));
208
+ if (!exportedHooks.has(hookName)) {
209
+ statements.push(exportAllFrom(`./${hookName}`));
210
+ exportedHooks.add(hookName);
211
+ }
183
212
  }
184
213
  // Add file header as leading comment on first statement
185
214
  if (statements.length > 0) {
@@ -1,5 +1,5 @@
1
1
  import * as t from '@babel/types';
2
- import { generateCode, addJSDocComment, typedParam } from './babel-ast';
2
+ import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
3
3
  import { buildCustomMutationString } from './schema-gql-ast';
4
4
  import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, createTypeTracker, } from './type-resolver';
5
5
  import { getGeneratedFileHeader } from './utils';
@@ -94,13 +94,13 @@ function generateCustomMutationHookInternal(options) {
94
94
  mutationOptions.push(t.objectProperty(t.identifier('mutationKey'), t.callExpression(t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), [])));
95
95
  }
96
96
  if (hasArgs) {
97
- mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)))], t.callExpression(t.identifier('execute'), [
98
- t.identifier(documentConstName),
99
- t.identifier('variables'),
97
+ mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)))], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('variables')], [
98
+ t.tsTypeReference(t.identifier(resultTypeName)),
99
+ t.tsTypeReference(t.identifier(variablesTypeName)),
100
100
  ]))));
101
101
  }
102
102
  else {
103
- mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [t.identifier(documentConstName)]))));
103
+ mutationOptions.push(t.objectProperty(t.identifier('mutationFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName)], [t.tsTypeReference(t.identifier(resultTypeName))]))));
104
104
  }
105
105
  mutationOptions.push(t.spreadElement(t.identifier('options')));
106
106
  hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)])));
@@ -1,5 +1,5 @@
1
1
  import * as t from '@babel/types';
2
- import { generateCode, addJSDocComment, typedParam } from './babel-ast';
2
+ import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
3
3
  import { buildCustomQueryString } from './schema-gql-ast';
4
4
  import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, createTypeTracker, } from './type-resolver';
5
5
  import { ucFirst, getGeneratedFileHeader } from './utils';
@@ -115,9 +115,9 @@ export function generateCustomQueryHook(options) {
115
115
  const useQueryOptions = [];
116
116
  if (hasArgs) {
117
117
  useQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')])));
118
- useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
119
- t.identifier(documentConstName),
120
- t.identifier('variables'),
118
+ useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('variables')], [
119
+ t.tsTypeReference(t.identifier(resultTypeName)),
120
+ t.tsTypeReference(t.identifier(variablesTypeName)),
121
121
  ]))));
122
122
  if (hasRequiredArgs) {
123
123
  useQueryOptions.push(t.objectProperty(t.identifier('enabled'), t.logicalExpression('&&', t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), t.binaryExpression('!==', t.optionalMemberExpression(t.identifier('options'), t.identifier('enabled'), false, true), t.booleanLiteral(false)))));
@@ -125,7 +125,7 @@ export function generateCustomQueryHook(options) {
125
125
  }
126
126
  else {
127
127
  useQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [])));
128
- useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [t.identifier(documentConstName)]))));
128
+ useQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName)], [t.tsTypeReference(t.identifier(resultTypeName))]))));
129
129
  }
130
130
  useQueryOptions.push(t.spreadElement(t.identifier('options')));
131
131
  hookBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('useQuery'), [t.objectExpression(useQueryOptions)])));
@@ -162,18 +162,13 @@ export function generateCustomQueryHook(options) {
162
162
  const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type));
163
163
  const fetchBodyStatements = [];
164
164
  if (hasArgs) {
165
- fetchBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('execute'), [
166
- t.identifier(documentConstName),
167
- t.identifier('variables'),
168
- t.identifier('options'),
165
+ fetchBodyStatements.push(t.returnStatement(createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')], [
166
+ t.tsTypeReference(t.identifier(resultTypeName)),
167
+ t.tsTypeReference(t.identifier(variablesTypeName)),
169
168
  ])));
170
169
  }
171
170
  else {
172
- fetchBodyStatements.push(t.returnStatement(t.callExpression(t.identifier('execute'), [
173
- t.identifier(documentConstName),
174
- t.identifier('undefined'),
175
- t.identifier('options'),
176
- ])));
171
+ fetchBodyStatements.push(t.returnStatement(createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')], [t.tsTypeReference(t.identifier(resultTypeName))])));
177
172
  }
178
173
  const fetchParams = [];
179
174
  if (hasArgs) {
@@ -201,19 +196,14 @@ export function generateCustomQueryHook(options) {
201
196
  const prefetchQueryOptions = [];
202
197
  if (hasArgs) {
203
198
  prefetchQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')])));
204
- prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
205
- t.identifier(documentConstName),
206
- t.identifier('variables'),
207
- t.identifier('options'),
199
+ prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')], [
200
+ t.tsTypeReference(t.identifier(resultTypeName)),
201
+ t.tsTypeReference(t.identifier(variablesTypeName)),
208
202
  ]))));
209
203
  }
210
204
  else {
211
205
  prefetchQueryOptions.push(t.objectProperty(t.identifier('queryKey'), t.callExpression(t.identifier(queryKeyName), [])));
212
- prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], t.callExpression(t.identifier('execute'), [
213
- t.identifier(documentConstName),
214
- t.identifier('undefined'),
215
- t.identifier('options'),
216
- ]))));
206
+ prefetchQueryOptions.push(t.objectProperty(t.identifier('queryFn'), t.arrowFunctionExpression([], createTypedCallExpression(t.identifier('execute'), [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')], [t.tsTypeReference(t.identifier(resultTypeName))]))));
217
207
  }
218
208
  prefetchBodyStatements.push(t.expressionStatement(t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), [t.objectExpression(prefetchQueryOptions)]))));
219
209
  const prefetchParams = [
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as t from 'gql-ast';
8
8
  import { print } from 'graphql';
9
- import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getOrderByTypeName, getScalarFields, getPrimaryKeyInfo, ucFirst, } from './utils';
9
+ import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getConditionTypeName, getOrderByTypeName, getScalarFields, getPrimaryKeyInfo, ucFirst, } from './utils';
10
10
  // ============================================================================
11
11
  // Field selection builders
12
12
  // ============================================================================
@@ -39,22 +39,39 @@ export function buildListQueryAST(config) {
39
39
  const { table } = config;
40
40
  const queryName = getAllRowsQueryName(table);
41
41
  const filterType = getFilterTypeName(table);
42
+ const conditionType = getConditionTypeName(table);
42
43
  const orderByType = getOrderByTypeName(table);
43
44
  const scalarFields = getScalarFields(table);
44
- // Variable definitions
45
+ // Variable definitions - all pagination arguments from PostGraphile
45
46
  const variableDefinitions = [
46
47
  t.variableDefinition({
47
48
  variable: t.variable({ name: 'first' }),
48
49
  type: t.namedType({ type: 'Int' }),
49
50
  }),
51
+ t.variableDefinition({
52
+ variable: t.variable({ name: 'last' }),
53
+ type: t.namedType({ type: 'Int' }),
54
+ }),
50
55
  t.variableDefinition({
51
56
  variable: t.variable({ name: 'offset' }),
52
57
  type: t.namedType({ type: 'Int' }),
53
58
  }),
59
+ t.variableDefinition({
60
+ variable: t.variable({ name: 'before' }),
61
+ type: t.namedType({ type: 'Cursor' }),
62
+ }),
63
+ t.variableDefinition({
64
+ variable: t.variable({ name: 'after' }),
65
+ type: t.namedType({ type: 'Cursor' }),
66
+ }),
54
67
  t.variableDefinition({
55
68
  variable: t.variable({ name: 'filter' }),
56
69
  type: t.namedType({ type: filterType }),
57
70
  }),
71
+ t.variableDefinition({
72
+ variable: t.variable({ name: 'condition' }),
73
+ type: t.namedType({ type: conditionType }),
74
+ }),
58
75
  t.variableDefinition({
59
76
  variable: t.variable({ name: 'orderBy' }),
60
77
  type: t.listType({
@@ -65,8 +82,12 @@ export function buildListQueryAST(config) {
65
82
  // Query arguments
66
83
  const args = [
67
84
  t.argument({ name: 'first', value: t.variable({ name: 'first' }) }),
85
+ t.argument({ name: 'last', value: t.variable({ name: 'last' }) }),
68
86
  t.argument({ name: 'offset', value: t.variable({ name: 'offset' }) }),
87
+ t.argument({ name: 'before', value: t.variable({ name: 'before' }) }),
88
+ t.argument({ name: 'after', value: t.variable({ name: 'after' }) }),
69
89
  t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }),
90
+ t.argument({ name: 'condition', value: t.variable({ name: 'condition' }) }),
70
91
  t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }),
71
92
  ];
72
93
  // Field selections
@@ -152,6 +152,7 @@ export function generate(options) {
152
152
  enumsFromSchemaTypes: generatedEnumNames,
153
153
  useCentralizedKeys,
154
154
  hasRelationships,
155
+ tableTypeNames,
155
156
  });
156
157
  for (const hook of mutationHooks) {
157
158
  files.push({
@@ -17,6 +17,8 @@ export interface MutationGeneratorOptions {
17
17
  enumsFromSchemaTypes?: string[];
18
18
  useCentralizedKeys?: boolean;
19
19
  hasRelationships?: boolean;
20
+ /** All table type names for determining which types to import from types.ts vs schema-types.ts */
21
+ tableTypeNames?: Set<string>;
20
22
  }
21
23
  export declare function generateCreateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
22
24
  export declare function generateUpdateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;