@constructive-io/graphql-codegen 2.21.0 → 2.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/codegen/barrel.d.ts +4 -1
- package/cli/codegen/barrel.js +18 -12
- package/cli/codegen/client.js +33 -0
- package/cli/codegen/custom-mutations.d.ts +4 -0
- package/cli/codegen/custom-mutations.js +39 -13
- package/cli/codegen/custom-queries.d.ts +4 -0
- package/cli/codegen/custom-queries.js +36 -11
- package/cli/codegen/gql-ast.js +9 -5
- package/cli/codegen/index.js +35 -7
- package/cli/codegen/mutations.d.ts +2 -0
- package/cli/codegen/mutations.js +87 -23
- package/cli/codegen/orm/barrel.js +4 -2
- package/cli/codegen/orm/index.js +17 -0
- package/cli/codegen/orm/input-types-generator.js +83 -29
- package/cli/codegen/orm/model-generator.js +6 -4
- package/cli/codegen/queries.js +36 -27
- package/cli/codegen/scalars.d.ts +6 -4
- package/cli/codegen/scalars.js +17 -9
- package/cli/codegen/schema-types-generator.d.ts +26 -0
- package/cli/codegen/schema-types-generator.js +365 -0
- package/cli/codegen/ts-ast.d.ts +3 -1
- package/cli/codegen/ts-ast.js +2 -2
- package/cli/codegen/type-resolver.d.ts +52 -6
- package/cli/codegen/type-resolver.js +97 -19
- package/cli/codegen/types.d.ts +7 -4
- package/cli/codegen/types.js +94 -41
- package/cli/codegen/utils.d.ts +20 -2
- package/cli/codegen/utils.js +32 -7
- package/cli/commands/generate-orm.js +5 -5
- package/cli/commands/generate.d.ts +4 -1
- package/cli/commands/generate.js +27 -8
- package/cli/introspect/transform-schema.d.ts +33 -21
- package/cli/introspect/transform-schema.js +31 -21
- package/esm/cli/codegen/barrel.d.ts +4 -1
- package/esm/cli/codegen/barrel.js +18 -12
- package/esm/cli/codegen/client.js +33 -0
- package/esm/cli/codegen/custom-mutations.d.ts +4 -0
- package/esm/cli/codegen/custom-mutations.js +40 -14
- package/esm/cli/codegen/custom-queries.d.ts +4 -0
- package/esm/cli/codegen/custom-queries.js +37 -12
- package/esm/cli/codegen/gql-ast.js +10 -6
- package/esm/cli/codegen/index.js +35 -7
- package/esm/cli/codegen/mutations.d.ts +2 -0
- package/esm/cli/codegen/mutations.js +88 -24
- package/esm/cli/codegen/orm/barrel.js +4 -2
- package/esm/cli/codegen/orm/index.js +17 -0
- package/esm/cli/codegen/orm/input-types-generator.js +83 -29
- package/esm/cli/codegen/orm/model-generator.js +7 -5
- package/esm/cli/codegen/queries.js +37 -28
- package/esm/cli/codegen/scalars.d.ts +6 -4
- package/esm/cli/codegen/scalars.js +16 -8
- package/esm/cli/codegen/schema-types-generator.d.ts +26 -0
- package/esm/cli/codegen/schema-types-generator.js +362 -0
- package/esm/cli/codegen/ts-ast.d.ts +3 -1
- package/esm/cli/codegen/ts-ast.js +2 -2
- package/esm/cli/codegen/type-resolver.d.ts +52 -6
- package/esm/cli/codegen/type-resolver.js +97 -20
- package/esm/cli/codegen/types.d.ts +7 -4
- package/esm/cli/codegen/types.js +95 -41
- package/esm/cli/codegen/utils.d.ts +20 -2
- package/esm/cli/codegen/utils.js +31 -7
- package/esm/cli/commands/generate-orm.js +5 -5
- package/esm/cli/commands/generate.d.ts +4 -1
- package/esm/cli/commands/generate.js +27 -8
- package/esm/cli/introspect/transform-schema.d.ts +33 -21
- package/esm/cli/introspect/transform-schema.js +31 -21
- package/esm/types/schema.d.ts +2 -0
- package/package.json +8 -7
- package/types/schema.d.ts +2 -0
- package/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/__tests__/codegen/input-types-generator.test.js +0 -635
- package/__tests__/codegen/react-query-optional.test.d.ts +0 -1
- package/__tests__/codegen/react-query-optional.test.js +0 -292
- package/cli/codegen/filters.d.ts +0 -27
- package/cli/codegen/filters.js +0 -357
- package/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/cli/codegen/orm/input-types-generator.test.js +0 -75
- package/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/cli/codegen/orm/select-types.test.js +0 -22
- package/cli/introspect/transform-schema.test.d.ts +0 -1
- package/cli/introspect/transform-schema.test.js +0 -67
- package/esm/__tests__/codegen/input-types-generator.test.d.ts +0 -1
- package/esm/__tests__/codegen/input-types-generator.test.js +0 -633
- package/esm/__tests__/codegen/react-query-optional.test.d.ts +0 -1
- package/esm/__tests__/codegen/react-query-optional.test.js +0 -290
- package/esm/cli/codegen/filters.d.ts +0 -27
- package/esm/cli/codegen/filters.js +0 -351
- package/esm/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
- package/esm/cli/codegen/orm/input-types-generator.test.js +0 -73
- package/esm/cli/codegen/orm/select-types.test.d.ts +0 -11
- package/esm/cli/codegen/orm/select-types.test.js +0 -21
- package/esm/cli/introspect/transform-schema.test.d.ts +0 -1
- package/esm/cli/introspect/transform-schema.test.js +0 -65
package/cli/codegen/mutations.js
CHANGED
|
@@ -7,6 +7,24 @@ exports.generateAllMutationHooks = generateAllMutationHooks;
|
|
|
7
7
|
const ts_ast_1 = require("./ts-ast");
|
|
8
8
|
const gql_ast_1 = require("./gql-ast");
|
|
9
9
|
const utils_1 = require("./utils");
|
|
10
|
+
/**
|
|
11
|
+
* Check if a field is auto-generated and should be excluded from create inputs
|
|
12
|
+
* Uses primary key from constraints and common timestamp patterns
|
|
13
|
+
*/
|
|
14
|
+
function isAutoGeneratedField(fieldName, pkFieldNames) {
|
|
15
|
+
const name = fieldName.toLowerCase();
|
|
16
|
+
// Exclude primary key fields (from constraints)
|
|
17
|
+
if (pkFieldNames.has(fieldName))
|
|
18
|
+
return true;
|
|
19
|
+
// Exclude common timestamp patterns (case-insensitive)
|
|
20
|
+
// These are typically auto-set by database triggers or defaults
|
|
21
|
+
const timestampPatterns = [
|
|
22
|
+
'createdat', 'created_at', 'createddate', 'created_date',
|
|
23
|
+
'updatedat', 'updated_at', 'updateddate', 'updated_date',
|
|
24
|
+
'deletedat', 'deleted_at', // soft delete timestamps
|
|
25
|
+
];
|
|
26
|
+
return timestampPatterns.includes(name);
|
|
27
|
+
}
|
|
10
28
|
// ============================================================================
|
|
11
29
|
// Create mutation hook generator
|
|
12
30
|
// ============================================================================
|
|
@@ -15,24 +33,35 @@ const utils_1 = require("./utils");
|
|
|
15
33
|
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
16
34
|
*/
|
|
17
35
|
function generateCreateMutationHook(table, options = {}) {
|
|
18
|
-
const { reactQueryEnabled = true } = options;
|
|
36
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [] } = options;
|
|
19
37
|
// Mutations require React Query - skip generation when disabled
|
|
20
38
|
if (!reactQueryEnabled) {
|
|
21
39
|
return null;
|
|
22
40
|
}
|
|
41
|
+
const enumSet = new Set(enumsFromSchemaTypes);
|
|
23
42
|
const project = (0, ts_ast_1.createProject)();
|
|
24
43
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
25
44
|
const hookName = (0, utils_1.getCreateMutationHookName)(table);
|
|
26
45
|
const mutationName = (0, utils_1.getCreateMutationName)(table);
|
|
27
46
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
47
|
+
// Get primary key field names dynamically from table constraints
|
|
48
|
+
const pkFieldNames = new Set((0, utils_1.getPrimaryKeyInfo)(table).map(pk => pk.name));
|
|
49
|
+
// Collect which enums are used by this table's fields
|
|
50
|
+
const usedEnums = new Set();
|
|
51
|
+
for (const field of scalarFields) {
|
|
52
|
+
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
53
|
+
if (enumSet.has(cleanType)) {
|
|
54
|
+
usedEnums.add(cleanType);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
28
57
|
// Generate GraphQL document via AST
|
|
29
58
|
const mutationAST = (0, gql_ast_1.buildCreateMutationAST)({ table });
|
|
30
59
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
31
60
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, (0, utils_1.getCreateMutationFileName)(table));
|
|
32
61
|
// Add file header
|
|
33
62
|
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(`Create mutation hook for ${typeName}`) + '\n\n');
|
|
34
|
-
//
|
|
35
|
-
|
|
63
|
+
// Build import declarations
|
|
64
|
+
const imports = [
|
|
36
65
|
(0, ts_ast_1.createImport)({
|
|
37
66
|
moduleSpecifier: '@tanstack/react-query',
|
|
38
67
|
namedImports: ['useMutation', 'useQueryClient'],
|
|
@@ -46,7 +75,16 @@ function generateCreateMutationHook(table, options = {}) {
|
|
|
46
75
|
moduleSpecifier: '../types',
|
|
47
76
|
typeOnlyNamedImports: [typeName],
|
|
48
77
|
}),
|
|
49
|
-
]
|
|
78
|
+
];
|
|
79
|
+
// Add import for enum types from schema-types if any are used
|
|
80
|
+
if (usedEnums.size > 0) {
|
|
81
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
82
|
+
moduleSpecifier: '../schema-types',
|
|
83
|
+
typeOnlyNamedImports: Array.from(usedEnums).sort(),
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
// Add imports
|
|
87
|
+
sourceFile.addImportDeclarations(imports);
|
|
50
88
|
// Re-export entity type
|
|
51
89
|
sourceFile.addStatements(`\n// Re-export entity type for convenience\nexport type { ${typeName} };\n`);
|
|
52
90
|
// Add section comment
|
|
@@ -60,11 +98,9 @@ function generateCreateMutationHook(table, options = {}) {
|
|
|
60
98
|
sourceFile.addStatements('// Types');
|
|
61
99
|
sourceFile.addStatements('// ============================================================================\n');
|
|
62
100
|
// Generate CreateInput type - exclude auto-generated fields
|
|
101
|
+
// Note: Not exported to avoid conflicts with schema-types
|
|
63
102
|
const inputFields = scalarFields
|
|
64
|
-
.filter((f) =>
|
|
65
|
-
const name = f.name.toLowerCase();
|
|
66
|
-
return !['id', 'createdat', 'updatedat', 'created_at', 'updated_at'].includes(name);
|
|
67
|
-
})
|
|
103
|
+
.filter((f) => !isAutoGeneratedField(f.name, pkFieldNames))
|
|
68
104
|
.map((f) => ({
|
|
69
105
|
name: f.name,
|
|
70
106
|
type: `${(0, utils_1.fieldTypeToTs)(f.type)} | null`,
|
|
@@ -72,6 +108,7 @@ function generateCreateMutationHook(table, options = {}) {
|
|
|
72
108
|
}));
|
|
73
109
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${typeName}CreateInput`, inputFields, {
|
|
74
110
|
docs: [`Input type for creating a ${typeName}`],
|
|
111
|
+
isExported: false,
|
|
75
112
|
}));
|
|
76
113
|
// Variables interface
|
|
77
114
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`, [
|
|
@@ -152,7 +189,7 @@ mutate({
|
|
|
152
189
|
* When reactQueryEnabled is false, returns null since mutations require React Query
|
|
153
190
|
*/
|
|
154
191
|
function generateUpdateMutationHook(table, options = {}) {
|
|
155
|
-
const { reactQueryEnabled = true } = options;
|
|
192
|
+
const { reactQueryEnabled = true, enumsFromSchemaTypes = [] } = options;
|
|
156
193
|
// Mutations require React Query - skip generation when disabled
|
|
157
194
|
if (!reactQueryEnabled) {
|
|
158
195
|
return null;
|
|
@@ -161,19 +198,32 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
161
198
|
if (table.query?.update === null) {
|
|
162
199
|
return null;
|
|
163
200
|
}
|
|
201
|
+
const enumSet = new Set(enumsFromSchemaTypes);
|
|
164
202
|
const project = (0, ts_ast_1.createProject)();
|
|
165
203
|
const { typeName, singularName } = (0, utils_1.getTableNames)(table);
|
|
166
204
|
const hookName = (0, utils_1.getUpdateMutationHookName)(table);
|
|
167
205
|
const mutationName = (0, utils_1.getUpdateMutationName)(table);
|
|
168
206
|
const scalarFields = (0, utils_1.getScalarFields)(table);
|
|
207
|
+
// Get primary key info dynamically from table constraints
|
|
208
|
+
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
209
|
+
const pkField = pkFields[0]; // Use first PK field
|
|
210
|
+
const pkFieldNames = new Set(pkFields.map(pk => pk.name));
|
|
211
|
+
// Collect which enums are used by this table's fields
|
|
212
|
+
const usedEnums = new Set();
|
|
213
|
+
for (const field of scalarFields) {
|
|
214
|
+
const cleanType = field.type.gqlType.replace(/!/g, '');
|
|
215
|
+
if (enumSet.has(cleanType)) {
|
|
216
|
+
usedEnums.add(cleanType);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
169
219
|
// Generate GraphQL document via AST
|
|
170
220
|
const mutationAST = (0, gql_ast_1.buildUpdateMutationAST)({ table });
|
|
171
221
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
172
222
|
const sourceFile = (0, ts_ast_1.createSourceFile)(project, (0, utils_1.getUpdateMutationFileName)(table));
|
|
173
223
|
// Add file header
|
|
174
224
|
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)(`Update mutation hook for ${typeName}`) + '\n\n');
|
|
175
|
-
//
|
|
176
|
-
|
|
225
|
+
// Build import declarations
|
|
226
|
+
const imports = [
|
|
177
227
|
(0, ts_ast_1.createImport)({
|
|
178
228
|
moduleSpecifier: '@tanstack/react-query',
|
|
179
229
|
namedImports: ['useMutation', 'useQueryClient'],
|
|
@@ -187,7 +237,16 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
187
237
|
moduleSpecifier: '../types',
|
|
188
238
|
typeOnlyNamedImports: [typeName],
|
|
189
239
|
}),
|
|
190
|
-
]
|
|
240
|
+
];
|
|
241
|
+
// Add import for enum types from schema-types if any are used
|
|
242
|
+
if (usedEnums.size > 0) {
|
|
243
|
+
imports.push((0, ts_ast_1.createImport)({
|
|
244
|
+
moduleSpecifier: '../schema-types',
|
|
245
|
+
typeOnlyNamedImports: Array.from(usedEnums).sort(),
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
// Add imports
|
|
249
|
+
sourceFile.addImportDeclarations(imports);
|
|
191
250
|
// Re-export entity type
|
|
192
251
|
sourceFile.addStatements(`\n// Re-export entity type for convenience\nexport type { ${typeName} };\n`);
|
|
193
252
|
// Add section comment
|
|
@@ -200,9 +259,10 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
200
259
|
sourceFile.addStatements('\n// ============================================================================');
|
|
201
260
|
sourceFile.addStatements('// Types');
|
|
202
261
|
sourceFile.addStatements('// ============================================================================\n');
|
|
203
|
-
// Generate Patch type - all fields optional
|
|
262
|
+
// Generate Patch type - all fields optional, exclude primary key
|
|
263
|
+
// Note: Not exported to avoid conflicts with schema-types
|
|
204
264
|
const patchFields = scalarFields
|
|
205
|
-
.filter((f) => f.name
|
|
265
|
+
.filter((f) => !pkFieldNames.has(f.name))
|
|
206
266
|
.map((f) => ({
|
|
207
267
|
name: f.name,
|
|
208
268
|
type: `${(0, utils_1.fieldTypeToTs)(f.type)} | null`,
|
|
@@ -210,13 +270,14 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
210
270
|
}));
|
|
211
271
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${typeName}Patch`, patchFields, {
|
|
212
272
|
docs: [`Patch type for updating a ${typeName} - all fields optional`],
|
|
273
|
+
isExported: false,
|
|
213
274
|
}));
|
|
214
|
-
// Variables interface
|
|
275
|
+
// Variables interface - use dynamic PK field name and type
|
|
215
276
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`, [
|
|
216
277
|
{
|
|
217
278
|
name: 'input',
|
|
218
279
|
type: `{
|
|
219
|
-
|
|
280
|
+
${pkField.name}: ${pkField.tsType};
|
|
220
281
|
patch: ${typeName}Patch;
|
|
221
282
|
}`,
|
|
222
283
|
},
|
|
@@ -255,7 +316,7 @@ function generateUpdateMutationHook(table, options = {}) {
|
|
|
255
316
|
),
|
|
256
317
|
onSuccess: (_, variables) => {
|
|
257
318
|
// Invalidate specific item and list queries
|
|
258
|
-
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'detail', variables.input.
|
|
319
|
+
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'detail', variables.input.${pkField.name}] });
|
|
259
320
|
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'list'] });
|
|
260
321
|
},
|
|
261
322
|
...options,
|
|
@@ -270,7 +331,7 @@ const { mutate, isPending } = ${hookName}();
|
|
|
270
331
|
|
|
271
332
|
mutate({
|
|
272
333
|
input: {
|
|
273
|
-
|
|
334
|
+
${pkField.name}: ${pkField.tsType === 'string' ? "'value-here'" : '123'},
|
|
274
335
|
patch: {
|
|
275
336
|
// ... fields to update
|
|
276
337
|
},
|
|
@@ -306,6 +367,9 @@ function generateDeleteMutationHook(table, options = {}) {
|
|
|
306
367
|
const { typeName } = (0, utils_1.getTableNames)(table);
|
|
307
368
|
const hookName = (0, utils_1.getDeleteMutationHookName)(table);
|
|
308
369
|
const mutationName = (0, utils_1.getDeleteMutationName)(table);
|
|
370
|
+
// Get primary key info dynamically from table constraints
|
|
371
|
+
const pkFields = (0, utils_1.getPrimaryKeyInfo)(table);
|
|
372
|
+
const pkField = pkFields[0]; // Use first PK field
|
|
309
373
|
// Generate GraphQL document via AST
|
|
310
374
|
const mutationAST = (0, gql_ast_1.buildDeleteMutationAST)({ table });
|
|
311
375
|
const mutationDocument = (0, gql_ast_1.printGraphQL)(mutationAST);
|
|
@@ -334,12 +398,12 @@ function generateDeleteMutationHook(table, options = {}) {
|
|
|
334
398
|
sourceFile.addStatements('\n// ============================================================================');
|
|
335
399
|
sourceFile.addStatements('// Types');
|
|
336
400
|
sourceFile.addStatements('// ============================================================================\n');
|
|
337
|
-
// Variables interface
|
|
401
|
+
// Variables interface - use dynamic PK field name and type
|
|
338
402
|
sourceFile.addInterface((0, ts_ast_1.createInterface)(`${(0, utils_1.ucFirst)(mutationName)}MutationVariables`, [
|
|
339
403
|
{
|
|
340
404
|
name: 'input',
|
|
341
405
|
type: `{
|
|
342
|
-
|
|
406
|
+
${pkField.name}: ${pkField.tsType};
|
|
343
407
|
}`,
|
|
344
408
|
},
|
|
345
409
|
]));
|
|
@@ -349,7 +413,7 @@ function generateDeleteMutationHook(table, options = {}) {
|
|
|
349
413
|
name: mutationName,
|
|
350
414
|
type: `{
|
|
351
415
|
clientMutationId: string | null;
|
|
352
|
-
|
|
416
|
+
deleted${(0, utils_1.ucFirst)(pkField.name)}: ${pkField.tsType} | null;
|
|
353
417
|
}`,
|
|
354
418
|
},
|
|
355
419
|
]));
|
|
@@ -378,7 +442,7 @@ function generateDeleteMutationHook(table, options = {}) {
|
|
|
378
442
|
),
|
|
379
443
|
onSuccess: (_, variables) => {
|
|
380
444
|
// Remove from cache and invalidate list
|
|
381
|
-
queryClient.removeQueries({ queryKey: ['${typeName.toLowerCase()}', 'detail', variables.input.
|
|
445
|
+
queryClient.removeQueries({ queryKey: ['${typeName.toLowerCase()}', 'detail', variables.input.${pkField.name}] });
|
|
382
446
|
queryClient.invalidateQueries({ queryKey: ['${typeName.toLowerCase()}', 'list'] });
|
|
383
447
|
},
|
|
384
448
|
...options,
|
|
@@ -393,7 +457,7 @@ const { mutate, isPending } = ${hookName}();
|
|
|
393
457
|
|
|
394
458
|
mutate({
|
|
395
459
|
input: {
|
|
396
|
-
|
|
460
|
+
${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'},
|
|
397
461
|
},
|
|
398
462
|
});
|
|
399
463
|
\`\`\``,
|
|
@@ -16,9 +16,11 @@ function generateModelsBarrel(tables) {
|
|
|
16
16
|
for (const table of tables) {
|
|
17
17
|
const { typeName } = (0, utils_1.getTableNames)(table);
|
|
18
18
|
const modelName = `${typeName}Model`;
|
|
19
|
-
|
|
19
|
+
// Use same naming logic as model-generator to avoid "index.ts" clash with barrel file
|
|
20
|
+
const baseFileName = (0, utils_1.lcFirst)(typeName);
|
|
21
|
+
const moduleFileName = baseFileName === 'index' ? `${baseFileName}Model` : baseFileName;
|
|
20
22
|
sourceFile.addExportDeclaration({
|
|
21
|
-
moduleSpecifier: `./${
|
|
23
|
+
moduleSpecifier: `./${moduleFileName}`,
|
|
22
24
|
namedExports: [modelName],
|
|
23
25
|
});
|
|
24
26
|
}
|
package/cli/codegen/orm/index.js
CHANGED
|
@@ -44,6 +44,23 @@ function generateOrm(options) {
|
|
|
44
44
|
];
|
|
45
45
|
const usedInputTypes = (0, input_types_generator_1.collectInputTypeNames)(allOps);
|
|
46
46
|
const usedPayloadTypes = (0, input_types_generator_1.collectPayloadTypeNames)(allOps);
|
|
47
|
+
// Also include payload types for table CRUD mutations (they reference Edge types)
|
|
48
|
+
if (typeRegistry) {
|
|
49
|
+
for (const table of tables) {
|
|
50
|
+
const typeName = table.name;
|
|
51
|
+
// Add standard CRUD payload types
|
|
52
|
+
const crudPayloadTypes = [
|
|
53
|
+
`Create${typeName}Payload`,
|
|
54
|
+
`Update${typeName}Payload`,
|
|
55
|
+
`Delete${typeName}Payload`,
|
|
56
|
+
];
|
|
57
|
+
for (const payloadType of crudPayloadTypes) {
|
|
58
|
+
if (typeRegistry.has(payloadType)) {
|
|
59
|
+
usedPayloadTypes.add(payloadType);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
47
64
|
const inputTypesFile = (0, input_types_generator_1.generateInputTypesFile)(typeRegistry ?? new Map(), usedInputTypes, tables, usedPayloadTypes);
|
|
48
65
|
files.push({ path: inputTypesFile.fileName, content: inputTypesFile.content });
|
|
49
66
|
}
|
|
@@ -122,6 +122,47 @@ function addScalarFilterTypes(sourceFile) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
// ============================================================================
|
|
125
|
+
// Enum Types Collector
|
|
126
|
+
// ============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* Check if a type is likely an enum (not a scalar and not ending with Input/Filter/etc)
|
|
129
|
+
*/
|
|
130
|
+
function isLikelyEnumType(typeName, typeRegistry) {
|
|
131
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
132
|
+
return typeInfo?.kind === 'ENUM';
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Collect enum types used by table fields
|
|
136
|
+
*/
|
|
137
|
+
function collectEnumTypesFromTables(tables, typeRegistry) {
|
|
138
|
+
const enumTypes = new Set();
|
|
139
|
+
for (const table of tables) {
|
|
140
|
+
for (const field of table.fields) {
|
|
141
|
+
const fieldType = typeof field.type === 'string' ? field.type : field.type.gqlType;
|
|
142
|
+
// Check if this type is an enum in the registry
|
|
143
|
+
if (isLikelyEnumType(fieldType, typeRegistry)) {
|
|
144
|
+
enumTypes.add(fieldType);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return enumTypes;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Add enum types to source file
|
|
152
|
+
*/
|
|
153
|
+
function addEnumTypes(sourceFile, typeRegistry, enumTypeNames) {
|
|
154
|
+
if (enumTypeNames.size === 0)
|
|
155
|
+
return;
|
|
156
|
+
(0, ts_ast_1.addSectionComment)(sourceFile, 'Enum Types');
|
|
157
|
+
for (const typeName of Array.from(enumTypeNames).sort()) {
|
|
158
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
159
|
+
if (!typeInfo || typeInfo.kind !== 'ENUM' || !typeInfo.enumValues)
|
|
160
|
+
continue;
|
|
161
|
+
const values = typeInfo.enumValues.map((v) => `'${v}'`).join(' | ');
|
|
162
|
+
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(typeName, values));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// ============================================================================
|
|
125
166
|
// Entity Types Generator (AST-based)
|
|
126
167
|
// ============================================================================
|
|
127
168
|
/**
|
|
@@ -387,10 +428,12 @@ function buildOrderByUnion(table) {
|
|
|
387
428
|
}
|
|
388
429
|
/**
|
|
389
430
|
* Add OrderBy types
|
|
431
|
+
* Uses inflection from table metadata for correct pluralization
|
|
390
432
|
*/
|
|
391
433
|
function addOrderByTypes(sourceFile, tables) {
|
|
392
434
|
(0, ts_ast_1.addSectionComment)(sourceFile, 'OrderBy Types');
|
|
393
435
|
for (const table of tables) {
|
|
436
|
+
// Use getOrderByTypeName which respects table.inflection.orderByType
|
|
394
437
|
const enumName = (0, utils_1.getOrderByTypeName)(table);
|
|
395
438
|
sourceFile.addTypeAlias((0, ts_ast_1.createTypeAlias)(enumName, buildOrderByUnion(table)));
|
|
396
439
|
}
|
|
@@ -509,32 +552,39 @@ function collectInputTypeNames(operations) {
|
|
|
509
552
|
}
|
|
510
553
|
return inputTypes;
|
|
511
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* Build a set of exact table CRUD input type names to skip
|
|
557
|
+
* These are generated by addAllCrudInputTypes, so we don't need to regenerate them
|
|
558
|
+
*/
|
|
559
|
+
function buildTableCrudTypeNames(tables) {
|
|
560
|
+
const crudTypes = new Set();
|
|
561
|
+
for (const table of tables) {
|
|
562
|
+
const { typeName } = (0, utils_1.getTableNames)(table);
|
|
563
|
+
crudTypes.add(`Create${typeName}Input`);
|
|
564
|
+
crudTypes.add(`Update${typeName}Input`);
|
|
565
|
+
crudTypes.add(`Delete${typeName}Input`);
|
|
566
|
+
crudTypes.add(`${typeName}Filter`);
|
|
567
|
+
crudTypes.add(`${typeName}Patch`);
|
|
568
|
+
}
|
|
569
|
+
return crudTypes;
|
|
570
|
+
}
|
|
512
571
|
/**
|
|
513
572
|
* Add custom input types from TypeRegistry
|
|
514
573
|
*/
|
|
515
|
-
function addCustomInputTypes(sourceFile, typeRegistry, usedInputTypes) {
|
|
574
|
+
function addCustomInputTypes(sourceFile, typeRegistry, usedInputTypes, tableCrudTypes) {
|
|
516
575
|
(0, ts_ast_1.addSectionComment)(sourceFile, 'Custom Input Types (from schema)');
|
|
517
576
|
const generatedTypes = new Set();
|
|
518
577
|
const typesToGenerate = new Set(Array.from(usedInputTypes));
|
|
519
|
-
// Filter out types we've already generated
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
typeName.startsWith('Update') ||
|
|
525
|
-
typeName.startsWith('Delete')) {
|
|
526
|
-
const isTableCrud = /^(Create|Update|Delete)[A-Z][a-zA-Z]+Input$/.test(typeName) ||
|
|
527
|
-
/^[A-Z][a-zA-Z]+Filter$/.test(typeName);
|
|
528
|
-
if (isTableCrud) {
|
|
529
|
-
typesToRemove.push(typeName);
|
|
578
|
+
// Filter out types we've already generated (exact matches for table CRUD types only)
|
|
579
|
+
if (tableCrudTypes) {
|
|
580
|
+
for (const typeName of Array.from(typesToGenerate)) {
|
|
581
|
+
if (tableCrudTypes.has(typeName)) {
|
|
582
|
+
typesToGenerate.delete(typeName);
|
|
530
583
|
}
|
|
531
584
|
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const maxIterations = 200;
|
|
536
|
-
while (typesToGenerate.size > 0 && iterations < maxIterations) {
|
|
537
|
-
iterations++;
|
|
585
|
+
}
|
|
586
|
+
// Process all types - no artificial limit
|
|
587
|
+
while (typesToGenerate.size > 0) {
|
|
538
588
|
const typeNameResult = typesToGenerate.values().next();
|
|
539
589
|
if (typeNameResult.done)
|
|
540
590
|
break;
|
|
@@ -600,10 +650,8 @@ function addPayloadTypes(sourceFile, typeRegistry, usedPayloadTypes, alreadyGene
|
|
|
600
650
|
'String', 'Int', 'Float', 'Boolean', 'ID', 'UUID', 'Datetime', 'Date',
|
|
601
651
|
'Time', 'JSON', 'BigInt', 'BigFloat', 'Cursor', 'Query', 'Mutation',
|
|
602
652
|
]);
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
while (typesToGenerate.size > 0 && iterations < maxIterations) {
|
|
606
|
-
iterations++;
|
|
653
|
+
// Process all types - no artificial limit
|
|
654
|
+
while (typesToGenerate.size > 0) {
|
|
607
655
|
const typeNameResult = typesToGenerate.values().next();
|
|
608
656
|
if (typeNameResult.done)
|
|
609
657
|
break;
|
|
@@ -670,7 +718,12 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
|
|
|
670
718
|
sourceFile.insertText(0, (0, ts_ast_1.createFileHeader)('GraphQL types for ORM client') + '\n');
|
|
671
719
|
// 1. Scalar filter types
|
|
672
720
|
addScalarFilterTypes(sourceFile);
|
|
673
|
-
// 2.
|
|
721
|
+
// 2. Enum types used by table fields
|
|
722
|
+
if (tables && tables.length > 0) {
|
|
723
|
+
const enumTypes = collectEnumTypesFromTables(tables, typeRegistry);
|
|
724
|
+
addEnumTypes(sourceFile, typeRegistry, enumTypes);
|
|
725
|
+
}
|
|
726
|
+
// 3. Entity and relation types (if tables provided)
|
|
674
727
|
if (tables && tables.length > 0) {
|
|
675
728
|
const tableByName = new Map(tables.map((table) => [table.name, table]));
|
|
676
729
|
addEntityTypes(sourceFile, tables);
|
|
@@ -678,16 +731,17 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
|
|
|
678
731
|
addEntityRelationTypes(sourceFile, tables, tableByName);
|
|
679
732
|
addEntityWithRelations(sourceFile, tables);
|
|
680
733
|
addEntitySelectTypes(sourceFile, tables, tableByName);
|
|
681
|
-
//
|
|
734
|
+
// 4. Table filter types
|
|
682
735
|
addTableFilterTypes(sourceFile, tables);
|
|
683
|
-
//
|
|
736
|
+
// 5. OrderBy types
|
|
684
737
|
addOrderByTypes(sourceFile, tables);
|
|
685
|
-
//
|
|
738
|
+
// 6. CRUD input types
|
|
686
739
|
addAllCrudInputTypes(sourceFile, tables);
|
|
687
740
|
}
|
|
688
|
-
//
|
|
689
|
-
|
|
690
|
-
|
|
741
|
+
// 7. Custom input types from TypeRegistry
|
|
742
|
+
const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined;
|
|
743
|
+
addCustomInputTypes(sourceFile, typeRegistry, usedInputTypes, tableCrudTypes);
|
|
744
|
+
// 8. Payload/return types for custom operations
|
|
691
745
|
if (usedPayloadTypes && usedPayloadTypes.size > 0) {
|
|
692
746
|
const alreadyGeneratedTypes = new Set();
|
|
693
747
|
if (tables) {
|
|
@@ -11,13 +11,15 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
11
11
|
const project = (0, ts_ast_1.createProject)();
|
|
12
12
|
const { typeName, singularName, pluralName } = (0, utils_1.getTableNames)(table);
|
|
13
13
|
const modelName = `${typeName}Model`;
|
|
14
|
-
|
|
14
|
+
// Avoid "index.ts" which clashes with barrel file
|
|
15
|
+
const baseFileName = (0, utils_1.lcFirst)(typeName);
|
|
16
|
+
const fileName = baseFileName === 'index' ? `${baseFileName}Model.ts` : `${baseFileName}.ts`;
|
|
15
17
|
const entityLower = singularName;
|
|
16
|
-
// Type names for this entity
|
|
18
|
+
// Type names for this entity - use inflection from table metadata
|
|
17
19
|
const selectTypeName = `${typeName}Select`;
|
|
18
20
|
const relationTypeName = `${typeName}WithRelations`;
|
|
19
|
-
const whereTypeName =
|
|
20
|
-
const orderByTypeName =
|
|
21
|
+
const whereTypeName = (0, utils_1.getFilterTypeName)(table);
|
|
22
|
+
const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
|
|
21
23
|
const createInputTypeName = `Create${typeName}Input`;
|
|
22
24
|
const updateInputTypeName = `Update${typeName}Input`;
|
|
23
25
|
const deleteInputTypeName = `Delete${typeName}Input`;
|