@constructive-io/graphql-codegen 4.47.10 → 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
@@ -103,38 +103,7 @@ function strictSelectGuard(selectTypeName) {
103
103
  t.tsTypeReference(t.identifier(selectTypeName)),
104
104
  ]));
105
105
  }
106
- /**
107
- * Discover extra required fields on a mutation input type beyond the standard
108
- * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
109
- * partitioned tables (e.g. databaseId as the partition key).
110
- */
111
- function getExtraInputKeys(inputTypeName, pkFieldNames, patchFieldName, typeRegistry) {
112
- if (!typeRegistry)
113
- return [];
114
- const inputType = typeRegistry.get(inputTypeName);
115
- if (!inputType || inputType.kind !== 'INPUT_OBJECT' || !inputType.inputFields)
116
- return [];
117
- const skip = new Set(['clientMutationId', ...(patchFieldName ? [patchFieldName] : [])]);
118
- for (const pk of pkFieldNames)
119
- skip.add(pk);
120
- const extras = [];
121
- for (const field of inputType.inputFields) {
122
- if (skip.has(field.name))
123
- continue;
124
- if (field.type.kind !== 'NON_NULL')
125
- continue;
126
- const innerName = field.type.ofType?.name;
127
- if (!innerName)
128
- continue;
129
- let tsType = 'string';
130
- if (innerName === 'Int' || innerName === 'Float' || innerName === 'BigFloat')
131
- tsType = 'number';
132
- else if (innerName === 'Boolean')
133
- tsType = 'boolean';
134
- extras.push({ name: field.name, gqlType: innerName, tsType });
135
- }
136
- return extras;
137
- }
106
+ // ExtraInputKey type and getExtraInputKeys are imported from ../utils
138
107
  function generateModelFile(table, _useSharedTypes, options, allTables, typeRegistry) {
139
108
  const { typeName, singularName, pluralName } = (0, utils_1.getTableNames)(table);
140
109
  const modelName = `${typeName}Model`;
@@ -434,7 +403,7 @@ function generateModelFile(table, _useSharedTypes, options, allTables, typeRegis
434
403
  // ── update ─────────────────────────────────────────────────────────────
435
404
  if (updateMutationName) {
436
405
  const patchFieldName = table.query?.patchFieldName ?? (0, utils_1.lcFirst)(typeName) + 'Patch';
437
- const updateExtraKeys = getExtraInputKeys(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
406
+ const updateExtraKeys = (0, utils_1.getExtraInputKeys)(updateInputTypeName, new Set(pkFields.map((pk) => pk.name)), patchFieldName, typeRegistry);
438
407
  const whereLiteral = () => t.tsTypeLiteral([
439
408
  (() => {
440
409
  const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType()));
@@ -490,7 +459,7 @@ function generateModelFile(table, _useSharedTypes, options, allTables, typeRegis
490
459
  }
491
460
  // ── delete ─────────────────────────────────────────────────────────────
492
461
  if (deleteMutationName) {
493
- const deleteExtraKeys = getExtraInputKeys(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
462
+ const deleteExtraKeys = (0, utils_1.getExtraInputKeys)(deleteInputTypeName, new Set(pkFields.map((pk) => pk.name)), null, typeRegistry);
494
463
  // Build where type with ALL PK fields + extra keys (supports composite PKs + partition keys)
495
464
  const whereLiteral = () => t.tsTypeLiteral([
496
465
  ...pkFields.map((pk) => {
@@ -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
@@ -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, getUpdateInputTypeName, 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,38 +66,7 @@ function strictSelectGuard(selectTypeName) {
66
66
  t.tsTypeReference(t.identifier(selectTypeName)),
67
67
  ]));
68
68
  }
69
- /**
70
- * Discover extra required fields on a mutation input type beyond the standard
71
- * ones (clientMutationId, PK fields, patch field). PostGraphile adds these for
72
- * partitioned tables (e.g. databaseId as the partition key).
73
- */
74
- function getExtraInputKeys(inputTypeName, pkFieldNames, patchFieldName, typeRegistry) {
75
- if (!typeRegistry)
76
- return [];
77
- const inputType = typeRegistry.get(inputTypeName);
78
- if (!inputType || inputType.kind !== 'INPUT_OBJECT' || !inputType.inputFields)
79
- return [];
80
- const skip = new Set(['clientMutationId', ...(patchFieldName ? [patchFieldName] : [])]);
81
- for (const pk of pkFieldNames)
82
- skip.add(pk);
83
- const extras = [];
84
- for (const field of inputType.inputFields) {
85
- if (skip.has(field.name))
86
- continue;
87
- if (field.type.kind !== 'NON_NULL')
88
- continue;
89
- const innerName = field.type.ofType?.name;
90
- if (!innerName)
91
- continue;
92
- let tsType = 'string';
93
- if (innerName === 'Int' || innerName === 'Float' || innerName === 'BigFloat')
94
- tsType = 'number';
95
- else if (innerName === 'Boolean')
96
- tsType = 'boolean';
97
- extras.push({ name: field.name, gqlType: innerName, tsType });
98
- }
99
- return extras;
100
- }
69
+ // ExtraInputKey type and getExtraInputKeys are imported from ../utils
101
70
  export function generateModelFile(table, _useSharedTypes, options, allTables, typeRegistry) {
102
71
  const { typeName, singularName, pluralName } = getTableNames(table);
103
72
  const modelName = `${typeName}Model`;
@@ -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.10",
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": "bca385f6d3d1311477e2c4ef36f9d49dec401c60"
103
+ "gitHead": "1dc015ea6eb2c968db440d87ca65d5e57dd4f8c3"
104
104
  }