@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.
- 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/index.js +1 -1
- package/core/codegen/orm/model-generator.d.ts +3 -3
- package/core/codegen/orm/model-generator.js +36 -13
- package/core/codegen/templates/query-builder.ts +2 -0
- 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/index.js +1 -1
- package/esm/core/codegen/orm/model-generator.d.ts +3 -3
- package/esm/core/codegen/orm/model-generator.js +37 -14
- 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
|
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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,
|
|
475
|
-
const keysObj = t.objectExpression(
|
|
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
|
},
|
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
|
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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,
|
|
438
|
-
const keysObj = t.objectExpression(
|
|
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.
|
|
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
|
}
|