@constructive-io/graphql-codegen 4.47.9 → 4.47.11

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.
@@ -138,6 +138,7 @@ function generate(options) {
138
138
  const mutationHooks = (0, mutations_1.generateAllMutationHooks)(tables, {
139
139
  reactQueryEnabled,
140
140
  useCentralizedKeys,
141
+ typeRegistry: customOperations?.typeRegistry,
141
142
  });
142
143
  for (const hook of mutationHooks) {
143
144
  files.push({
@@ -1,4 +1,4 @@
1
- import type { Table } from '../../types/schema';
1
+ import type { Table, TypeRegistry } from '../../types/schema';
2
2
  export interface GeneratedMutationFile {
3
3
  fileName: string;
4
4
  content: string;
@@ -6,6 +6,7 @@ export interface GeneratedMutationFile {
6
6
  export interface MutationGeneratorOptions {
7
7
  reactQueryEnabled?: boolean;
8
8
  useCentralizedKeys?: boolean;
9
+ typeRegistry?: TypeRegistry;
9
10
  }
10
11
  export declare function generateCreateMutationHook(table: Table, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
11
12
  export declare function generateUpdateMutationHook(table: Table, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
@@ -172,7 +172,7 @@ function generateCreateMutationHook(table, options = {}) {
172
172
  };
173
173
  }
174
174
  function generateUpdateMutationHook(table, options = {}) {
175
- const { reactQueryEnabled = true, useCentralizedKeys = true } = options;
175
+ const { reactQueryEnabled = true, useCentralizedKeys = true, typeRegistry } = options;
176
176
  if (!reactQueryEnabled)
177
177
  return null;
178
178
  if (table.query?.update === null)
@@ -191,6 +191,8 @@ function generateUpdateMutationHook(table, options = {}) {
191
191
  const pkField = pkFields[0];
192
192
  const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
193
193
  const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword();
194
+ const updateInputTypeName = (0, utils_1.getUpdateInputTypeName)(table);
195
+ const updateExtraKeys = (0, utils_1.getExtraInputKeys)(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
194
196
  const statements = [];
195
197
  // Imports
196
198
  statements.push((0, hooks_ast_1.createImportDeclaration)('@tanstack/react-query', [
@@ -209,9 +211,12 @@ function generateUpdateMutationHook(table, options = {}) {
209
211
  statements.push((0, hooks_ast_1.createImportDeclaration)('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
210
212
  // Re-exports
211
213
  statements.push((0, hooks_ast_1.createTypeReExport)([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types'));
212
- // Variable type: { pkField: type; patchFieldName: PatchType }
214
+ // Variable type: { pkField: type; extraKeys...; patchFieldName: PatchType }
213
215
  const updateVarType = t.tsTypeLiteral([
214
216
  t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)),
217
+ ...updateExtraKeys.map((ek) => t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(ek.tsType === 'number' ? t.tsNumberKeyword() :
218
+ ek.tsType === 'boolean' ? t.tsBooleanKeyword() :
219
+ t.tsStringKeyword()))),
215
220
  t.tsPropertySignature(t.identifier(patchFieldName), t.tsTypeAnnotation((0, hooks_ast_1.typeRef)(patchTypeName))),
216
221
  ]);
217
222
  const resultType = (sel) => buildMutationResultType(mutationName, singularName, relationTypeName, sel);
@@ -258,11 +263,15 @@ function generateUpdateMutationHook(table, options = {}) {
258
263
  // getClient().singular.update({ where: { pkField }, data: patchFieldName, select: ... }).unwrap()
259
264
  const destructParam = t.objectPattern([
260
265
  (0, hooks_ast_1.shorthandProp)(pkField.name),
266
+ ...updateExtraKeys.map((ek) => (0, hooks_ast_1.shorthandProp)(ek.name)),
261
267
  (0, hooks_ast_1.shorthandProp)(patchFieldName),
262
268
  ]);
263
269
  destructParam.typeAnnotation = t.tsTypeAnnotation(updateVarType);
264
270
  const mutationFnExpr = t.arrowFunctionExpression([destructParam], (0, hooks_ast_1.getClientCallUnwrap)(singularName, 'update', t.objectExpression([
265
- (0, hooks_ast_1.objectProp)('where', t.objectExpression([(0, hooks_ast_1.shorthandProp)(pkField.name)])),
271
+ (0, hooks_ast_1.objectProp)('where', t.objectExpression([
272
+ (0, hooks_ast_1.shorthandProp)(pkField.name),
273
+ ...updateExtraKeys.map((ek) => (0, hooks_ast_1.shorthandProp)(ek.name)),
274
+ ])),
266
275
  (0, hooks_ast_1.objectProp)('data', t.identifier(patchFieldName)),
267
276
  (0, hooks_ast_1.objectProp)('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
268
277
  ])));
@@ -299,7 +308,7 @@ function generateUpdateMutationHook(table, options = {}) {
299
308
  };
300
309
  }
301
310
  function generateDeleteMutationHook(table, options = {}) {
302
- const { reactQueryEnabled = true, useCentralizedKeys = true } = options;
311
+ const { reactQueryEnabled = true, useCentralizedKeys = true, typeRegistry } = options;
303
312
  if (!reactQueryEnabled)
304
313
  return null;
305
314
  if (table.query?.delete === null)
@@ -316,6 +325,8 @@ function generateDeleteMutationHook(table, options = {}) {
316
325
  const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
317
326
  const pkField = pkFields[0];
318
327
  const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword();
328
+ const deleteInputTypeName = (0, utils_1.getDeleteInputTypeName)(table);
329
+ const deleteExtraKeys = (0, utils_1.getExtraInputKeys)(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
319
330
  const statements = [];
320
331
  // Imports
321
332
  statements.push((0, hooks_ast_1.createImportDeclaration)('@tanstack/react-query', [
@@ -334,9 +345,12 @@ function generateDeleteMutationHook(table, options = {}) {
334
345
  statements.push((0, hooks_ast_1.createImportDeclaration)('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
335
346
  // Re-exports
336
347
  statements.push((0, hooks_ast_1.createTypeReExport)([selectTypeName, relationTypeName], '../../orm/input-types'));
337
- // Variable type: { pkField: type }
348
+ // Variable type: { pkField: type; extraKeys... }
338
349
  const deleteVarType = t.tsTypeLiteral([
339
350
  t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)),
351
+ ...deleteExtraKeys.map((ek) => t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(ek.tsType === 'number' ? t.tsNumberKeyword() :
352
+ ek.tsType === 'boolean' ? t.tsBooleanKeyword() :
353
+ t.tsStringKeyword()))),
340
354
  ]);
341
355
  const resultType = (sel) => buildMutationResultType(mutationName, singularName, relationTypeName, sel);
342
356
  // Overload 1: with fields
@@ -380,10 +394,16 @@ function generateDeleteMutationHook(table, options = {}) {
380
394
  : undefined;
381
395
  // mutationFn: ({ pkField }: { pkField: type }) =>
382
396
  // getClient().singular.delete({ where: { pkField }, select: ... }).unwrap()
383
- const destructParam = t.objectPattern([(0, hooks_ast_1.shorthandProp)(pkField.name)]);
397
+ const destructParam = t.objectPattern([
398
+ (0, hooks_ast_1.shorthandProp)(pkField.name),
399
+ ...deleteExtraKeys.map((ek) => (0, hooks_ast_1.shorthandProp)(ek.name)),
400
+ ]);
384
401
  destructParam.typeAnnotation = t.tsTypeAnnotation(deleteVarType);
385
402
  const mutationFnExpr = t.arrowFunctionExpression([destructParam], (0, hooks_ast_1.getClientCallUnwrap)(singularName, 'delete', t.objectExpression([
386
- (0, hooks_ast_1.objectProp)('where', t.objectExpression([(0, hooks_ast_1.shorthandProp)(pkField.name)])),
403
+ (0, hooks_ast_1.objectProp)('where', t.objectExpression([
404
+ (0, hooks_ast_1.shorthandProp)(pkField.name),
405
+ ...deleteExtraKeys.map((ek) => (0, hooks_ast_1.shorthandProp)(ek.name)),
406
+ ])),
387
407
  (0, hooks_ast_1.objectProp)('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
388
408
  ])));
389
409
  // onSuccess: remove detail, invalidate lists
@@ -35,7 +35,7 @@ function generateOrm(options) {
35
35
  content: selectTypesFile.content,
36
36
  });
37
37
  // 2. Generate model files
38
- const modelFiles = (0, model_generator_1.generateAllModelFiles)(tables, useSharedTypes);
38
+ const modelFiles = (0, model_generator_1.generateAllModelFiles)(tables, useSharedTypes, typeRegistry);
39
39
  for (const modelFile of modelFiles) {
40
40
  files.push({
41
41
  path: `models/${modelFile.fileName}`,
@@ -1,9 +1,9 @@
1
- import type { Table } from '../../../types/schema';
1
+ import type { Table, TypeRegistry } from '../../../types/schema';
2
2
  export interface GeneratedModelFile {
3
3
  fileName: string;
4
4
  content: string;
5
5
  modelName: string;
6
6
  tableName: string;
7
7
  }
8
- export declare function generateModelFile(table: Table, _useSharedTypes: boolean, options?: Record<string, never>, allTables?: Table[]): GeneratedModelFile;
9
- export declare function generateAllModelFiles(tables: Table[], useSharedTypes: boolean): GeneratedModelFile[];
8
+ export declare function generateModelFile(table: Table, _useSharedTypes: boolean, options?: Record<string, never>, allTables?: Table[], typeRegistry?: TypeRegistry): GeneratedModelFile;
9
+ export declare function generateAllModelFiles(tables: Table[], useSharedTypes: boolean, typeRegistry?: TypeRegistry): GeneratedModelFile[];
@@ -103,7 +103,8 @@ function strictSelectGuard(selectTypeName) {
103
103
  t.tsTypeReference(t.identifier(selectTypeName)),
104
104
  ]));
105
105
  }
106
- function generateModelFile(table, _useSharedTypes, options, allTables) {
106
+ // ExtraInputKey type and getExtraInputKeys are imported from ../utils
107
+ function generateModelFile(table, _useSharedTypes, options, allTables, typeRegistry) {
107
108
  const { typeName, singularName, pluralName } = (0, utils_1.getTableNames)(table);
108
109
  const modelName = `${typeName}Model`;
109
110
  const baseFileName = (0, utils_1.lcFirst)(typeName);
@@ -114,7 +115,7 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
114
115
  const whereTypeName = (0, utils_1.getFilterTypeName)(table);
115
116
  const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
116
117
  const createInputTypeName = `Create${typeName}Input`;
117
- const updateInputTypeName = `Update${typeName}Input`;
118
+ const updateInputTypeName = (0, utils_1.getUpdateInputTypeName)(table);
118
119
  const patchTypeName = `${typeName}Patch`;
119
120
  const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
120
121
  const pkField = pkFields[0];
@@ -401,12 +402,19 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
401
402
  }
402
403
  // ── update ─────────────────────────────────────────────────────────────
403
404
  if (updateMutationName) {
405
+ const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
406
+ const updateExtraKeys = (0, utils_1.getExtraInputKeys)(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
404
407
  const whereLiteral = () => t.tsTypeLiteral([
405
408
  (() => {
406
409
  const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType()));
407
410
  prop.optional = false;
408
411
  return prop;
409
412
  })(),
413
+ ...updateExtraKeys.map((ek) => {
414
+ const prop = t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(tsTypeFromPrimitive(ek.tsType)));
415
+ prop.optional = false;
416
+ return prop;
417
+ }),
410
418
  ]);
411
419
  const argsType = (sel) => t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([
412
420
  sel,
@@ -430,7 +438,10 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
430
438
  strictSelectGuard(selectTypeName),
431
439
  ]));
432
440
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
433
- const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
441
+ // Build extraKeys object for partitioned table fields
442
+ const extraKeysArg = updateExtraKeys.length > 0
443
+ ? t.objectExpression(updateExtraKeys.map((ek) => t.objectProperty(t.identifier(ek.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(ek.name)))))
444
+ : t.identifier('undefined');
434
445
  const bodyArgs = [
435
446
  t.stringLiteral(typeName),
436
447
  t.stringLiteral(updateMutationName),
@@ -442,17 +453,26 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
442
453
  t.stringLiteral(pkField.name),
443
454
  t.stringLiteral(patchFieldName),
444
455
  t.identifier('connectionFieldsMap'),
456
+ extraKeysArg,
445
457
  ];
446
458
  classBody.push(createClassMethod('update', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName)));
447
459
  }
448
460
  // ── delete ─────────────────────────────────────────────────────────────
449
461
  if (deleteMutationName) {
450
- // Build where type with ALL PK fields (supports composite PKs)
451
- const whereLiteral = () => t.tsTypeLiteral(pkFields.map((pk) => {
452
- const prop = t.tsPropertySignature(t.identifier(pk.name), t.tsTypeAnnotation(tsTypeFromPrimitive(pk.tsType ?? 'string')));
453
- prop.optional = false;
454
- return prop;
455
- }));
462
+ const deleteExtraKeys = (0, utils_1.getExtraInputKeys)(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
463
+ // Build where type with ALL PK fields + extra keys (supports composite PKs + partition keys)
464
+ const whereLiteral = () => t.tsTypeLiteral([
465
+ ...pkFields.map((pk) => {
466
+ const prop = t.tsPropertySignature(t.identifier(pk.name), t.tsTypeAnnotation(tsTypeFromPrimitive(pk.tsType ?? 'string')));
467
+ prop.optional = false;
468
+ return prop;
469
+ }),
470
+ ...deleteExtraKeys.map((ek) => {
471
+ const prop = t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(tsTypeFromPrimitive(ek.tsType)));
472
+ prop.optional = false;
473
+ return prop;
474
+ }),
475
+ ]);
456
476
  const argsType = (sel) => t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([whereLiteral(), sel]));
457
477
  const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
458
478
  t.tsTypeLiteral([
@@ -471,8 +491,11 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
471
491
  strictSelectGuard(selectTypeName),
472
492
  ]));
473
493
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
474
- // Build keys object: { field1: args.where.field1, field2: args.where.field2, ... }
475
- const keysObj = t.objectExpression(pkFields.map((pk) => t.objectProperty(t.identifier(pk.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(pk.name)))));
494
+ // Build keys object: { field1: args.where.field1, ... } including extra keys
495
+ const keysObj = t.objectExpression([
496
+ ...pkFields.map((pk) => t.objectProperty(t.identifier(pk.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(pk.name)))),
497
+ ...deleteExtraKeys.map((ek) => t.objectProperty(t.identifier(ek.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(ek.name)))),
498
+ ]);
476
499
  const bodyArgs = [
477
500
  t.stringLiteral(typeName),
478
501
  t.stringLiteral(deleteMutationName),
@@ -748,6 +771,6 @@ function generateModelFile(table, _useSharedTypes, options, allTables) {
748
771
  tableName: table.name,
749
772
  };
750
773
  }
751
- function generateAllModelFiles(tables, useSharedTypes) {
752
- return tables.map((table) => generateModelFile(table, useSharedTypes, undefined, tables));
774
+ function generateAllModelFiles(tables, useSharedTypes, typeRegistry) {
775
+ return tables.map((table) => generateModelFile(table, useSharedTypes, undefined, tables, typeRegistry));
753
776
  }
@@ -509,6 +509,7 @@ export function buildUpdateByPkDocument<TSelect, TData>(
509
509
  idFieldName: string,
510
510
  patchFieldName: string,
511
511
  connectionFieldsMap?: Record<string, Record<string, string>>,
512
+ extraKeys?: Record<string, unknown>,
512
513
  ): { document: string; variables: Record<string, unknown> } {
513
514
  const selections = select
514
515
  ? buildSelections(
@@ -533,6 +534,7 @@ export function buildUpdateByPkDocument<TSelect, TData>(
533
534
  variables: {
534
535
  input: {
535
536
  [idFieldName]: id,
537
+ ...extraKeys,
536
538
  [patchFieldName]: data,
537
539
  },
538
540
  },
@@ -146,6 +146,17 @@ export declare function getUpdateInputTypeName(table: Table): string;
146
146
  * like DeletePostTagByPostIdAndTagIdInput), falls back to Delete${Entity}Input.
147
147
  */
148
148
  export declare function getDeleteInputTypeName(table: Table): string;
149
+ export interface ExtraInputKey {
150
+ name: string;
151
+ gqlType: string;
152
+ tsType: string;
153
+ }
154
+ /**
155
+ * Discover extra required fields on a mutation input type beyond the standard
156
+ * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
157
+ * partitioned tables (e.g. databaseId as the partition key).
158
+ */
159
+ export declare function getExtraInputKeys(inputTypeName: string, pkFieldNames: Set<string>, patchFieldName: string | null, typeRegistry?: TypeRegistry): ExtraInputKey[];
149
160
  /**
150
161
  * Convert GraphQL type to TypeScript type
151
162
  */
@@ -35,6 +35,7 @@ exports.getCreateInputTypeName = getCreateInputTypeName;
35
35
  exports.getPatchTypeName = getPatchTypeName;
36
36
  exports.getUpdateInputTypeName = getUpdateInputTypeName;
37
37
  exports.getDeleteInputTypeName = getDeleteInputTypeName;
38
+ exports.getExtraInputKeys = getExtraInputKeys;
38
39
  exports.gqlTypeToTs = gqlTypeToTs;
39
40
  exports.fieldTypeToTs = fieldTypeToTs;
40
41
  exports.getScalarFilterType = getScalarFilterType;
@@ -295,6 +296,38 @@ function getDeleteInputTypeName(table) {
295
296
  const mutationName = table.query?.delete;
296
297
  return mutationName ? (0, inflekt_1.ucFirst)(mutationName) + 'Input' : `Delete${table.name}Input`;
297
298
  }
299
+ /**
300
+ * Discover extra required fields on a mutation input type beyond the standard
301
+ * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
302
+ * partitioned tables (e.g. databaseId as the partition key).
303
+ */
304
+ function getExtraInputKeys(inputTypeName, pkFieldNames, patchFieldName, typeRegistry) {
305
+ if (!typeRegistry)
306
+ return [];
307
+ const inputType = typeRegistry.get(inputTypeName);
308
+ if (!inputType || inputType.kind !== 'INPUT_OBJECT' || !inputType.inputFields)
309
+ return [];
310
+ const skip = new Set(['clientMutationId', ...(patchFieldName ? [patchFieldName] : [])]);
311
+ for (const pk of pkFieldNames)
312
+ skip.add(pk);
313
+ const extras = [];
314
+ for (const field of inputType.inputFields) {
315
+ if (skip.has(field.name))
316
+ continue;
317
+ if (field.type.kind !== 'NON_NULL')
318
+ continue;
319
+ const innerName = field.type.ofType?.name;
320
+ if (!innerName)
321
+ continue;
322
+ let tsType = 'string';
323
+ if (innerName === 'Int' || innerName === 'Float' || innerName === 'BigFloat')
324
+ tsType = 'number';
325
+ else if (innerName === 'Boolean')
326
+ tsType = 'boolean';
327
+ extras.push({ name: field.name, gqlType: innerName, tsType });
328
+ }
329
+ return extras;
330
+ }
298
331
  // ============================================================================
299
332
  // Type mapping: GraphQL → TypeScript
300
333
  // ============================================================================
@@ -133,6 +133,7 @@ export function generate(options) {
133
133
  const mutationHooks = generateAllMutationHooks(tables, {
134
134
  reactQueryEnabled,
135
135
  useCentralizedKeys,
136
+ typeRegistry: customOperations?.typeRegistry,
136
137
  });
137
138
  for (const hook of mutationHooks) {
138
139
  files.push({
@@ -1,4 +1,4 @@
1
- import type { Table } from '../../types/schema';
1
+ import type { Table, TypeRegistry } from '../../types/schema';
2
2
  export interface GeneratedMutationFile {
3
3
  fileName: string;
4
4
  content: string;
@@ -6,6 +6,7 @@ export interface GeneratedMutationFile {
6
6
  export interface MutationGeneratorOptions {
7
7
  reactQueryEnabled?: boolean;
8
8
  useCentralizedKeys?: boolean;
9
+ typeRegistry?: TypeRegistry;
9
10
  }
10
11
  export declare function generateCreateMutationHook(table: Table, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
11
12
  export declare function generateUpdateMutationHook(table: Table, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import * as t from '@babel/types';
11
11
  import { addJSDocComment, buildSelectionArgsCall, callExpr, constDecl, createFunctionParam, createImportDeclaration, createSTypeParam, createTypeReExport, destructureParamsWithSelection, exportDeclareFunction, exportFunction, generateHookFileCode, getClientCallUnwrap, inferSelectResultType, objectProp, omitType, returnUseMutation, selectionConfigType, shorthandProp, spreadObj, sRef, typeRef, typeLiteralWithProps, useMutationOptionsType, useMutationResultType, voidStatement, } from './hooks-ast';
12
- import { getBulkCreateMutationFileName, getBulkCreateMutationHookName, getBulkDeleteMutationFileName, getBulkDeleteMutationHookName, getBulkUpdateMutationFileName, getBulkUpdateMutationHookName, getBulkUpsertMutationFileName, getBulkUpsertMutationHookName, getCreateMutationFileName, getCreateMutationHookName, getCreateMutationName, getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, getPrimaryKeyInfo, getTableNames, getUpdateMutationFileName, getUpdateMutationHookName, getUpdateMutationName, hasValidPrimaryKey, lcFirst, } from './utils';
12
+ import { getBulkCreateMutationFileName, getBulkCreateMutationHookName, getBulkDeleteMutationFileName, getBulkDeleteMutationHookName, getBulkUpdateMutationFileName, getBulkUpdateMutationHookName, getBulkUpsertMutationFileName, getBulkUpsertMutationHookName, getCreateMutationFileName, getCreateMutationHookName, getCreateMutationName, getDeleteInputTypeName, getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, getExtraInputKeys, getPrimaryKeyInfo, getTableNames, getUpdateInputTypeName, getUpdateMutationFileName, getUpdateMutationHookName, getUpdateMutationName, hasValidPrimaryKey, lcFirst, } from './utils';
13
13
  function buildMutationResultType(mutationName, singularName, relationTypeName, selectType) {
14
14
  return typeLiteralWithProps([
15
15
  {
@@ -133,7 +133,7 @@ export function generateCreateMutationHook(table, options = {}) {
133
133
  };
134
134
  }
135
135
  export function generateUpdateMutationHook(table, options = {}) {
136
- const { reactQueryEnabled = true, useCentralizedKeys = true } = options;
136
+ const { reactQueryEnabled = true, useCentralizedKeys = true, typeRegistry } = options;
137
137
  if (!reactQueryEnabled)
138
138
  return null;
139
139
  if (table.query?.update === null)
@@ -152,6 +152,8 @@ export function generateUpdateMutationHook(table, options = {}) {
152
152
  const pkField = pkFields[0];
153
153
  const patchFieldName = table.query?.patchFieldName ?? lcFirst(typeName) + 'Patch';
154
154
  const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword();
155
+ const updateInputTypeName = getUpdateInputTypeName(table);
156
+ const updateExtraKeys = getExtraInputKeys(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
155
157
  const statements = [];
156
158
  // Imports
157
159
  statements.push(createImportDeclaration('@tanstack/react-query', [
@@ -170,9 +172,12 @@ export function generateUpdateMutationHook(table, options = {}) {
170
172
  statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
171
173
  // Re-exports
172
174
  statements.push(createTypeReExport([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types'));
173
- // Variable type: { pkField: type; patchFieldName: PatchType }
175
+ // Variable type: { pkField: type; extraKeys...; patchFieldName: PatchType }
174
176
  const updateVarType = t.tsTypeLiteral([
175
177
  t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)),
178
+ ...updateExtraKeys.map((ek) => t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(ek.tsType === 'number' ? t.tsNumberKeyword() :
179
+ ek.tsType === 'boolean' ? t.tsBooleanKeyword() :
180
+ t.tsStringKeyword()))),
176
181
  t.tsPropertySignature(t.identifier(patchFieldName), t.tsTypeAnnotation(typeRef(patchTypeName))),
177
182
  ]);
178
183
  const resultType = (sel) => buildMutationResultType(mutationName, singularName, relationTypeName, sel);
@@ -219,11 +224,15 @@ export function generateUpdateMutationHook(table, options = {}) {
219
224
  // getClient().singular.update({ where: { pkField }, data: patchFieldName, select: ... }).unwrap()
220
225
  const destructParam = t.objectPattern([
221
226
  shorthandProp(pkField.name),
227
+ ...updateExtraKeys.map((ek) => shorthandProp(ek.name)),
222
228
  shorthandProp(patchFieldName),
223
229
  ]);
224
230
  destructParam.typeAnnotation = t.tsTypeAnnotation(updateVarType);
225
231
  const mutationFnExpr = t.arrowFunctionExpression([destructParam], getClientCallUnwrap(singularName, 'update', t.objectExpression([
226
- objectProp('where', t.objectExpression([shorthandProp(pkField.name)])),
232
+ objectProp('where', t.objectExpression([
233
+ shorthandProp(pkField.name),
234
+ ...updateExtraKeys.map((ek) => shorthandProp(ek.name)),
235
+ ])),
227
236
  objectProp('data', t.identifier(patchFieldName)),
228
237
  objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
229
238
  ])));
@@ -260,7 +269,7 @@ export function generateUpdateMutationHook(table, options = {}) {
260
269
  };
261
270
  }
262
271
  export function generateDeleteMutationHook(table, options = {}) {
263
- const { reactQueryEnabled = true, useCentralizedKeys = true } = options;
272
+ const { reactQueryEnabled = true, useCentralizedKeys = true, typeRegistry } = options;
264
273
  if (!reactQueryEnabled)
265
274
  return null;
266
275
  if (table.query?.delete === null)
@@ -277,6 +286,8 @@ export function generateDeleteMutationHook(table, options = {}) {
277
286
  const pkFields = getPrimaryKeyInfo(table);
278
287
  const pkField = pkFields[0];
279
288
  const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword();
289
+ const deleteInputTypeName = getDeleteInputTypeName(table);
290
+ const deleteExtraKeys = getExtraInputKeys(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
280
291
  const statements = [];
281
292
  // Imports
282
293
  statements.push(createImportDeclaration('@tanstack/react-query', [
@@ -295,9 +306,12 @@ export function generateDeleteMutationHook(table, options = {}) {
295
306
  statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'HookStrictSelect'], true));
296
307
  // Re-exports
297
308
  statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types'));
298
- // Variable type: { pkField: type }
309
+ // Variable type: { pkField: type; extraKeys... }
299
310
  const deleteVarType = t.tsTypeLiteral([
300
311
  t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)),
312
+ ...deleteExtraKeys.map((ek) => t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(ek.tsType === 'number' ? t.tsNumberKeyword() :
313
+ ek.tsType === 'boolean' ? t.tsBooleanKeyword() :
314
+ t.tsStringKeyword()))),
301
315
  ]);
302
316
  const resultType = (sel) => buildMutationResultType(mutationName, singularName, relationTypeName, sel);
303
317
  // Overload 1: with fields
@@ -341,10 +355,16 @@ export function generateDeleteMutationHook(table, options = {}) {
341
355
  : undefined;
342
356
  // mutationFn: ({ pkField }: { pkField: type }) =>
343
357
  // getClient().singular.delete({ where: { pkField }, select: ... }).unwrap()
344
- const destructParam = t.objectPattern([shorthandProp(pkField.name)]);
358
+ const destructParam = t.objectPattern([
359
+ shorthandProp(pkField.name),
360
+ ...deleteExtraKeys.map((ek) => shorthandProp(ek.name)),
361
+ ]);
345
362
  destructParam.typeAnnotation = t.tsTypeAnnotation(deleteVarType);
346
363
  const mutationFnExpr = t.arrowFunctionExpression([destructParam], getClientCallUnwrap(singularName, 'delete', t.objectExpression([
347
- objectProp('where', t.objectExpression([shorthandProp(pkField.name)])),
364
+ objectProp('where', t.objectExpression([
365
+ shorthandProp(pkField.name),
366
+ ...deleteExtraKeys.map((ek) => shorthandProp(ek.name)),
367
+ ])),
348
368
  objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))),
349
369
  ])));
350
370
  // onSuccess: remove detail, invalidate lists
@@ -31,7 +31,7 @@ export function generateOrm(options) {
31
31
  content: selectTypesFile.content,
32
32
  });
33
33
  // 2. Generate model files
34
- const modelFiles = generateAllModelFiles(tables, useSharedTypes);
34
+ const modelFiles = generateAllModelFiles(tables, useSharedTypes, typeRegistry);
35
35
  for (const modelFile of modelFiles) {
36
36
  files.push({
37
37
  path: `models/${modelFile.fileName}`,
@@ -1,9 +1,9 @@
1
- import type { Table } from '../../../types/schema';
1
+ import type { Table, TypeRegistry } from '../../../types/schema';
2
2
  export interface GeneratedModelFile {
3
3
  fileName: string;
4
4
  content: string;
5
5
  modelName: string;
6
6
  tableName: string;
7
7
  }
8
- export declare function generateModelFile(table: Table, _useSharedTypes: boolean, options?: Record<string, never>, allTables?: Table[]): GeneratedModelFile;
9
- export declare function generateAllModelFiles(tables: Table[], useSharedTypes: boolean): GeneratedModelFile[];
8
+ export declare function generateModelFile(table: Table, _useSharedTypes: boolean, options?: Record<string, never>, allTables?: Table[], typeRegistry?: TypeRegistry): GeneratedModelFile;
9
+ export declare function generateAllModelFiles(tables: Table[], useSharedTypes: boolean, typeRegistry?: TypeRegistry): GeneratedModelFile[];
@@ -7,7 +7,7 @@
7
7
  import * as t from '@babel/types';
8
8
  import { singularize } from 'inflekt';
9
9
  import { generateCode } from '../babel-ast';
10
- import { getCreateInputTypeName, getCreateMutationName, getDeleteInputTypeName, getDeleteMutationName, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, hasValidPrimaryKey, lcFirst, ucFirst, } from '../utils';
10
+ import { getCreateInputTypeName, getCreateMutationName, getDeleteInputTypeName, getDeleteMutationName, getExtraInputKeys, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, getPrimaryKeyInfo, getTableNames, getUpdateInputTypeName, hasValidPrimaryKey, lcFirst, ucFirst, } from '../utils';
11
11
  function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
12
12
  const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
13
13
  const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier));
@@ -66,7 +66,8 @@ function strictSelectGuard(selectTypeName) {
66
66
  t.tsTypeReference(t.identifier(selectTypeName)),
67
67
  ]));
68
68
  }
69
- export function generateModelFile(table, _useSharedTypes, options, allTables) {
69
+ // ExtraInputKey type and getExtraInputKeys are imported from ../utils
70
+ export function generateModelFile(table, _useSharedTypes, options, allTables, typeRegistry) {
70
71
  const { typeName, singularName, pluralName } = getTableNames(table);
71
72
  const modelName = `${typeName}Model`;
72
73
  const baseFileName = lcFirst(typeName);
@@ -77,7 +78,7 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
77
78
  const whereTypeName = getFilterTypeName(table);
78
79
  const orderByTypeName = getOrderByTypeName(table);
79
80
  const createInputTypeName = `Create${typeName}Input`;
80
- const updateInputTypeName = `Update${typeName}Input`;
81
+ const updateInputTypeName = getUpdateInputTypeName(table);
81
82
  const patchTypeName = `${typeName}Patch`;
82
83
  const pkFields = getPrimaryKeyInfo(table);
83
84
  const pkField = pkFields[0];
@@ -364,12 +365,19 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
364
365
  }
365
366
  // ── update ─────────────────────────────────────────────────────────────
366
367
  if (updateMutationName) {
368
+ const patchFieldName = table.query?.patchFieldName ?? lcFirst(typeName) + 'Patch';
369
+ const updateExtraKeys = getExtraInputKeys(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
367
370
  const whereLiteral = () => t.tsTypeLiteral([
368
371
  (() => {
369
372
  const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType()));
370
373
  prop.optional = false;
371
374
  return prop;
372
375
  })(),
376
+ ...updateExtraKeys.map((ek) => {
377
+ const prop = t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(tsTypeFromPrimitive(ek.tsType)));
378
+ prop.optional = false;
379
+ return prop;
380
+ }),
373
381
  ]);
374
382
  const argsType = (sel) => t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([
375
383
  sel,
@@ -393,7 +401,10 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
393
401
  strictSelectGuard(selectTypeName),
394
402
  ]));
395
403
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
396
- const patchFieldName = table.query?.patchFieldName ?? lcFirst(typeName) + 'Patch';
404
+ // Build extraKeys object for partitioned table fields
405
+ const extraKeysArg = updateExtraKeys.length > 0
406
+ ? t.objectExpression(updateExtraKeys.map((ek) => t.objectProperty(t.identifier(ek.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(ek.name)))))
407
+ : t.identifier('undefined');
397
408
  const bodyArgs = [
398
409
  t.stringLiteral(typeName),
399
410
  t.stringLiteral(updateMutationName),
@@ -405,17 +416,26 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
405
416
  t.stringLiteral(pkField.name),
406
417
  t.stringLiteral(patchFieldName),
407
418
  t.identifier('connectionFieldsMap'),
419
+ extraKeysArg,
408
420
  ];
409
421
  classBody.push(createClassMethod('update', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName)));
410
422
  }
411
423
  // ── delete ─────────────────────────────────────────────────────────────
412
424
  if (deleteMutationName) {
413
- // Build where type with ALL PK fields (supports composite PKs)
414
- const whereLiteral = () => t.tsTypeLiteral(pkFields.map((pk) => {
415
- const prop = t.tsPropertySignature(t.identifier(pk.name), t.tsTypeAnnotation(tsTypeFromPrimitive(pk.tsType ?? 'string')));
416
- prop.optional = false;
417
- return prop;
418
- }));
425
+ const deleteExtraKeys = getExtraInputKeys(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
426
+ // Build where type with ALL PK fields + extra keys (supports composite PKs + partition keys)
427
+ const whereLiteral = () => t.tsTypeLiteral([
428
+ ...pkFields.map((pk) => {
429
+ const prop = t.tsPropertySignature(t.identifier(pk.name), t.tsTypeAnnotation(tsTypeFromPrimitive(pk.tsType ?? 'string')));
430
+ prop.optional = false;
431
+ return prop;
432
+ }),
433
+ ...deleteExtraKeys.map((ek) => {
434
+ const prop = t.tsPropertySignature(t.identifier(ek.name), t.tsTypeAnnotation(tsTypeFromPrimitive(ek.tsType)));
435
+ prop.optional = false;
436
+ return prop;
437
+ }),
438
+ ]);
419
439
  const argsType = (sel) => t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([whereLiteral(), sel]));
420
440
  const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
421
441
  t.tsTypeLiteral([
@@ -434,8 +454,11 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
434
454
  strictSelectGuard(selectTypeName),
435
455
  ]));
436
456
  const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
437
- // Build keys object: { field1: args.where.field1, field2: args.where.field2, ... }
438
- const keysObj = t.objectExpression(pkFields.map((pk) => t.objectProperty(t.identifier(pk.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(pk.name)))));
457
+ // Build keys object: { field1: args.where.field1, ... } including extra keys
458
+ const keysObj = t.objectExpression([
459
+ ...pkFields.map((pk) => t.objectProperty(t.identifier(pk.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(pk.name)))),
460
+ ...deleteExtraKeys.map((ek) => t.objectProperty(t.identifier(ek.name), t.memberExpression(t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(ek.name)))),
461
+ ]);
439
462
  const bodyArgs = [
440
463
  t.stringLiteral(typeName),
441
464
  t.stringLiteral(deleteMutationName),
@@ -711,6 +734,6 @@ export function generateModelFile(table, _useSharedTypes, options, allTables) {
711
734
  tableName: table.name,
712
735
  };
713
736
  }
714
- export function generateAllModelFiles(tables, useSharedTypes) {
715
- return tables.map((table) => generateModelFile(table, useSharedTypes, undefined, tables));
737
+ export function generateAllModelFiles(tables, useSharedTypes, typeRegistry) {
738
+ return tables.map((table) => generateModelFile(table, useSharedTypes, undefined, tables, typeRegistry));
716
739
  }
@@ -146,6 +146,17 @@ export declare function getUpdateInputTypeName(table: Table): string;
146
146
  * like DeletePostTagByPostIdAndTagIdInput), falls back to Delete${Entity}Input.
147
147
  */
148
148
  export declare function getDeleteInputTypeName(table: Table): string;
149
+ export interface ExtraInputKey {
150
+ name: string;
151
+ gqlType: string;
152
+ tsType: string;
153
+ }
154
+ /**
155
+ * Discover extra required fields on a mutation input type beyond the standard
156
+ * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
157
+ * partitioned tables (e.g. databaseId as the partition key).
158
+ */
159
+ export declare function getExtraInputKeys(inputTypeName: string, pkFieldNames: Set<string>, patchFieldName: string | null, typeRegistry?: TypeRegistry): ExtraInputKey[];
149
160
  /**
150
161
  * Convert GraphQL type to TypeScript type
151
162
  */
@@ -240,6 +240,38 @@ export function getDeleteInputTypeName(table) {
240
240
  const mutationName = table.query?.delete;
241
241
  return mutationName ? ucFirst(mutationName) + 'Input' : `Delete${table.name}Input`;
242
242
  }
243
+ /**
244
+ * Discover extra required fields on a mutation input type beyond the standard
245
+ * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
246
+ * partitioned tables (e.g. databaseId as the partition key).
247
+ */
248
+ export function getExtraInputKeys(inputTypeName, pkFieldNames, patchFieldName, typeRegistry) {
249
+ if (!typeRegistry)
250
+ return [];
251
+ const inputType = typeRegistry.get(inputTypeName);
252
+ if (!inputType || inputType.kind !== 'INPUT_OBJECT' || !inputType.inputFields)
253
+ return [];
254
+ const skip = new Set(['clientMutationId', ...(patchFieldName ? [patchFieldName] : [])]);
255
+ for (const pk of pkFieldNames)
256
+ skip.add(pk);
257
+ const extras = [];
258
+ for (const field of inputType.inputFields) {
259
+ if (skip.has(field.name))
260
+ continue;
261
+ if (field.type.kind !== 'NON_NULL')
262
+ continue;
263
+ const innerName = field.type.ofType?.name;
264
+ if (!innerName)
265
+ continue;
266
+ let tsType = 'string';
267
+ if (innerName === 'Int' || innerName === 'Float' || innerName === 'BigFloat')
268
+ tsType = 'number';
269
+ else if (innerName === 'Boolean')
270
+ tsType = 'boolean';
271
+ extras.push({ name: field.name, gqlType: innerName, tsType });
272
+ }
273
+ return extras;
274
+ }
243
275
  // ============================================================================
244
276
  // Type mapping: GraphQL → TypeScript
245
277
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-codegen",
3
- "version": "4.47.9",
3
+ "version": "4.47.11",
4
4
  "description": "GraphQL SDK generator for Constructive databases with React Query hooks",
5
5
  "keywords": [
6
6
  "graphql",
@@ -100,5 +100,5 @@
100
100
  "tsx": "^4.21.0",
101
101
  "typescript": "^5.9.3"
102
102
  },
103
- "gitHead": "16c2ae1c30850db1d71c021346c9d34361361d8e"
103
+ "gitHead": "1dc015ea6eb2c968db440d87ca65d5e57dd4f8c3"
104
104
  }