@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.
- package/core/codegen/index.js +1 -0
- package/core/codegen/mutations.d.ts +2 -1
- package/core/codegen/mutations.js +27 -7
- package/core/codegen/orm/model-generator.js +3 -34
- package/core/codegen/utils.d.ts +11 -0
- package/core/codegen/utils.js +33 -0
- package/esm/core/codegen/index.js +1 -0
- package/esm/core/codegen/mutations.d.ts +2 -1
- package/esm/core/codegen/mutations.js +28 -8
- package/esm/core/codegen/orm/model-generator.js +2 -33
- package/esm/core/codegen/utils.d.ts +11 -0
- package/esm/core/codegen/utils.js +32 -0
- package/package.json +2 -2
package/core/codegen/index.js
CHANGED
|
@@ -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([
|
|
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([
|
|
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([
|
|
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) => {
|
package/core/codegen/utils.d.ts
CHANGED
|
@@ -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
|
*/
|
package/core/codegen/utils.js
CHANGED
|
@@ -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([
|
|
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([
|
|
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([
|
|
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.
|
|
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": "
|
|
103
|
+
"gitHead": "1dc015ea6eb2c968db440d87ca65d5e57dd4f8c3"
|
|
104
104
|
}
|