@constructive-io/graphql-codegen 4.9.0 → 4.13.0
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/cli/arg-mapper.d.ts +1 -1
- package/core/codegen/cli/arg-mapper.js +15 -11
- package/core/codegen/cli/command-map-generator.d.ts +1 -0
- package/core/codegen/cli/command-map-generator.js +4 -0
- package/core/codegen/cli/config-command-generator.d.ts +11 -0
- package/core/codegen/cli/config-command-generator.js +458 -0
- package/core/codegen/cli/custom-command-generator.js +4 -3
- package/core/codegen/cli/docs-generator.d.ts +6 -5
- package/core/codegen/cli/docs-generator.js +167 -64
- package/core/codegen/cli/helpers-generator.d.ts +15 -0
- package/core/codegen/cli/helpers-generator.js +119 -0
- package/core/codegen/cli/index.d.ts +4 -0
- package/core/codegen/cli/index.js +21 -3
- package/core/codegen/cli/table-command-generator.d.ts +15 -0
- package/core/codegen/cli/table-command-generator.js +20 -1
- package/core/codegen/docs-utils.d.ts +26 -1
- package/core/codegen/docs-utils.js +105 -0
- package/core/codegen/orm/index.js +3 -2
- package/core/codegen/orm/input-types-generator.d.ts +3 -1
- package/core/codegen/orm/input-types-generator.js +123 -17
- package/core/codegen/orm/model-generator.d.ts +6 -2
- package/core/codegen/orm/model-generator.js +59 -29
- package/core/codegen/orm/select-types.d.ts +4 -2
- package/core/codegen/scalars.js +8 -0
- package/core/codegen/templates/cli-entry.ts +2 -2
- package/core/codegen/templates/cli-utils.ts +28 -0
- package/core/codegen/templates/query-builder.ts +28 -5
- package/core/codegen/templates/select-types.ts +4 -2
- package/core/generate.js +14 -4
- package/esm/core/codegen/cli/arg-mapper.d.ts +1 -1
- package/esm/core/codegen/cli/arg-mapper.js +15 -11
- package/esm/core/codegen/cli/command-map-generator.d.ts +1 -0
- package/esm/core/codegen/cli/command-map-generator.js +4 -0
- package/esm/core/codegen/cli/config-command-generator.d.ts +11 -0
- package/esm/core/codegen/cli/config-command-generator.js +422 -0
- package/esm/core/codegen/cli/custom-command-generator.js +4 -3
- package/esm/core/codegen/cli/docs-generator.d.ts +6 -5
- package/esm/core/codegen/cli/docs-generator.js +168 -65
- package/esm/core/codegen/cli/helpers-generator.d.ts +15 -0
- package/esm/core/codegen/cli/helpers-generator.js +83 -0
- package/esm/core/codegen/cli/index.d.ts +4 -0
- package/esm/core/codegen/cli/index.js +18 -2
- package/esm/core/codegen/cli/table-command-generator.d.ts +15 -0
- package/esm/core/codegen/cli/table-command-generator.js +20 -3
- package/esm/core/codegen/docs-utils.d.ts +26 -1
- package/esm/core/codegen/docs-utils.js +102 -0
- package/esm/core/codegen/orm/index.js +3 -2
- package/esm/core/codegen/orm/input-types-generator.d.ts +3 -1
- package/esm/core/codegen/orm/input-types-generator.js +123 -17
- package/esm/core/codegen/orm/model-generator.d.ts +6 -2
- package/esm/core/codegen/orm/model-generator.js +59 -29
- package/esm/core/codegen/orm/select-types.d.ts +4 -2
- package/esm/core/codegen/scalars.js +8 -0
- package/esm/core/generate.js +14 -4
- package/esm/types/config.d.ts +9 -0
- package/esm/types/config.js +1 -0
- package/package.json +11 -11
- package/types/config.d.ts +9 -0
- package/types/config.js +1 -0
|
@@ -252,6 +252,11 @@ const SCALAR_FILTER_CONFIGS = [
|
|
|
252
252
|
operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'],
|
|
253
253
|
},
|
|
254
254
|
{ name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] },
|
|
255
|
+
// VectorFilter: equality/distinct operators for vector columns on Filter types.
|
|
256
|
+
// Similarity search uses condition types (embeddingNearby), not filters, but
|
|
257
|
+
// connection-filter may still generate a filter for vector columns. This ensures
|
|
258
|
+
// the generated type uses number[] rather than being silently omitted.
|
|
259
|
+
{ name: 'VectorFilter', tsType: 'number[]', operators: ['equality', 'distinct'] },
|
|
255
260
|
// List filters (for array fields like string[], int[], uuid[])
|
|
256
261
|
{
|
|
257
262
|
name: 'StringListFilter',
|
|
@@ -748,10 +753,15 @@ function generateTableFilterTypes(tables) {
|
|
|
748
753
|
// ============================================================================
|
|
749
754
|
/**
|
|
750
755
|
* Build properties for a table condition interface
|
|
751
|
-
* Condition types are simpler than Filter types - they use direct value equality
|
|
756
|
+
* Condition types are simpler than Filter types - they use direct value equality.
|
|
757
|
+
*
|
|
758
|
+
* Also merges any extra fields from the GraphQL schema's condition type
|
|
759
|
+
* (e.g., plugin-injected fields like vectorEmbedding from VectorSearchPlugin)
|
|
760
|
+
* that are not derived from the table's own columns.
|
|
752
761
|
*/
|
|
753
|
-
function buildTableConditionProperties(table) {
|
|
762
|
+
function buildTableConditionProperties(table, typeRegistry) {
|
|
754
763
|
const properties = [];
|
|
764
|
+
const generatedFieldNames = new Set();
|
|
755
765
|
for (const field of table.fields) {
|
|
756
766
|
const fieldType = typeof field.type === 'string' ? field.type : field.type.gqlType;
|
|
757
767
|
if ((0, utils_1.isRelationField)(field.name, table))
|
|
@@ -763,17 +773,38 @@ function buildTableConditionProperties(table) {
|
|
|
763
773
|
type: `${tsType} | null`,
|
|
764
774
|
optional: true,
|
|
765
775
|
});
|
|
776
|
+
generatedFieldNames.add(field.name);
|
|
777
|
+
}
|
|
778
|
+
// Merge any additional fields from the schema's condition type
|
|
779
|
+
// (e.g., plugin-added fields like vectorEmbedding from VectorSearchPlugin)
|
|
780
|
+
if (typeRegistry) {
|
|
781
|
+
const conditionTypeName = (0, utils_1.getConditionTypeName)(table);
|
|
782
|
+
const conditionType = typeRegistry.get(conditionTypeName);
|
|
783
|
+
if (conditionType?.kind === 'INPUT_OBJECT' &&
|
|
784
|
+
conditionType.inputFields) {
|
|
785
|
+
for (const field of conditionType.inputFields) {
|
|
786
|
+
if (generatedFieldNames.has(field.name))
|
|
787
|
+
continue;
|
|
788
|
+
const tsType = typeRefToTs(field.type);
|
|
789
|
+
properties.push({
|
|
790
|
+
name: field.name,
|
|
791
|
+
type: tsType,
|
|
792
|
+
optional: true,
|
|
793
|
+
description: (0, utils_1.stripSmartComments)(field.description, true),
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}
|
|
766
797
|
}
|
|
767
798
|
return properties;
|
|
768
799
|
}
|
|
769
800
|
/**
|
|
770
801
|
* Generate table condition type statements
|
|
771
802
|
*/
|
|
772
|
-
function generateTableConditionTypes(tables) {
|
|
803
|
+
function generateTableConditionTypes(tables, typeRegistry) {
|
|
773
804
|
const statements = [];
|
|
774
805
|
for (const table of tables) {
|
|
775
806
|
const conditionName = (0, utils_1.getConditionTypeName)(table);
|
|
776
|
-
statements.push(createExportedInterface(conditionName, buildTableConditionProperties(table)));
|
|
807
|
+
statements.push(createExportedInterface(conditionName, buildTableConditionProperties(table, typeRegistry)));
|
|
777
808
|
}
|
|
778
809
|
if (statements.length > 0) {
|
|
779
810
|
addSectionComment(statements, 'Table Condition Types');
|
|
@@ -784,9 +815,13 @@ function generateTableConditionTypes(tables) {
|
|
|
784
815
|
// OrderBy Types Generator (AST-based)
|
|
785
816
|
// ============================================================================
|
|
786
817
|
/**
|
|
787
|
-
* Build OrderBy union type values
|
|
818
|
+
* Build OrderBy union type values.
|
|
819
|
+
*
|
|
820
|
+
* Also merges any extra values from the GraphQL schema's orderBy enum
|
|
821
|
+
* (e.g., plugin-injected values like EMBEDDING_DISTANCE_ASC/DESC
|
|
822
|
+
* from VectorSearchPlugin).
|
|
788
823
|
*/
|
|
789
|
-
function buildOrderByValues(table) {
|
|
824
|
+
function buildOrderByValues(table, typeRegistry) {
|
|
790
825
|
const values = ['PRIMARY_KEY_ASC', 'PRIMARY_KEY_DESC', 'NATURAL'];
|
|
791
826
|
for (const field of table.fields) {
|
|
792
827
|
if ((0, utils_1.isRelationField)(field.name, table))
|
|
@@ -795,16 +830,30 @@ function buildOrderByValues(table) {
|
|
|
795
830
|
values.push(`${upperSnake}_ASC`);
|
|
796
831
|
values.push(`${upperSnake}_DESC`);
|
|
797
832
|
}
|
|
833
|
+
// Merge any additional values from the schema's orderBy enum type
|
|
834
|
+
// (e.g., plugin-added values like EMBEDDING_DISTANCE_ASC/DESC)
|
|
835
|
+
if (typeRegistry) {
|
|
836
|
+
const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
|
|
837
|
+
const orderByType = typeRegistry.get(orderByTypeName);
|
|
838
|
+
if (orderByType?.kind === 'ENUM' && orderByType.enumValues) {
|
|
839
|
+
const existingValues = new Set(values);
|
|
840
|
+
for (const value of orderByType.enumValues) {
|
|
841
|
+
if (!existingValues.has(value)) {
|
|
842
|
+
values.push(value);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
798
847
|
return values;
|
|
799
848
|
}
|
|
800
849
|
/**
|
|
801
850
|
* Generate OrderBy type statements
|
|
802
851
|
*/
|
|
803
|
-
function generateOrderByTypes(tables) {
|
|
852
|
+
function generateOrderByTypes(tables, typeRegistry) {
|
|
804
853
|
const statements = [];
|
|
805
854
|
for (const table of tables) {
|
|
806
855
|
const enumName = (0, utils_1.getOrderByTypeName)(table);
|
|
807
|
-
const values = buildOrderByValues(table);
|
|
856
|
+
const values = buildOrderByValues(table, typeRegistry);
|
|
808
857
|
const unionType = createStringLiteralUnion(values);
|
|
809
858
|
const typeAlias = t.tsTypeAliasDeclaration(t.identifier(enumName), null, unionType);
|
|
810
859
|
statements.push(t.exportNamedDeclaration(typeAlias));
|
|
@@ -1013,9 +1062,9 @@ function buildTableCrudTypeNames(tables) {
|
|
|
1013
1062
|
/**
|
|
1014
1063
|
* Generate custom input type statements from TypeRegistry
|
|
1015
1064
|
*/
|
|
1016
|
-
function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes, comments = true) {
|
|
1065
|
+
function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes, comments = true, alreadyGeneratedTypes) {
|
|
1017
1066
|
const statements = [];
|
|
1018
|
-
const generatedTypes = new Set();
|
|
1067
|
+
const generatedTypes = new Set(alreadyGeneratedTypes ?? []);
|
|
1019
1068
|
const typesToGenerate = new Set(Array.from(usedInputTypes));
|
|
1020
1069
|
// Filter out types we've already generated (exact matches for table CRUD types only)
|
|
1021
1070
|
if (tableCrudTypes) {
|
|
@@ -1055,11 +1104,12 @@ function generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes,
|
|
|
1055
1104
|
optional,
|
|
1056
1105
|
description: (0, utils_1.stripSmartComments)(field.description, comments),
|
|
1057
1106
|
});
|
|
1058
|
-
// Follow nested Input
|
|
1107
|
+
// Follow nested types (Input objects, Enums, etc.) that exist in the registry
|
|
1059
1108
|
const baseType = (0, type_resolver_1.getTypeBaseName)(field.type);
|
|
1060
1109
|
if (baseType &&
|
|
1061
|
-
|
|
1062
|
-
!generatedTypes.has(baseType)
|
|
1110
|
+
!scalars_1.SCALAR_NAMES.has(baseType) &&
|
|
1111
|
+
!generatedTypes.has(baseType) &&
|
|
1112
|
+
typeRegistry.has(baseType)) {
|
|
1063
1113
|
typesToGenerate.add(baseType);
|
|
1064
1114
|
}
|
|
1065
1115
|
}
|
|
@@ -1243,12 +1293,49 @@ function generateConnectionFieldsMap(tables, tableByName) {
|
|
|
1243
1293
|
return statements;
|
|
1244
1294
|
}
|
|
1245
1295
|
// ============================================================================
|
|
1296
|
+
// Plugin-Injected Type Collector
|
|
1297
|
+
// ============================================================================
|
|
1298
|
+
/**
|
|
1299
|
+
* Collect extra input type names referenced by plugin-injected condition fields.
|
|
1300
|
+
*
|
|
1301
|
+
* When plugins (like VectorSearchPlugin) inject fields into condition types,
|
|
1302
|
+
* they reference types (like VectorNearbyInput, VectorMetric) that also need
|
|
1303
|
+
* to be generated. This function discovers those types by comparing the
|
|
1304
|
+
* schema's condition type fields against the table's own columns.
|
|
1305
|
+
*/
|
|
1306
|
+
function collectConditionExtraInputTypes(tables, typeRegistry) {
|
|
1307
|
+
const extraTypes = new Set();
|
|
1308
|
+
for (const table of tables) {
|
|
1309
|
+
const conditionTypeName = (0, utils_1.getConditionTypeName)(table);
|
|
1310
|
+
const conditionType = typeRegistry.get(conditionTypeName);
|
|
1311
|
+
if (!conditionType ||
|
|
1312
|
+
conditionType.kind !== 'INPUT_OBJECT' ||
|
|
1313
|
+
!conditionType.inputFields) {
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const tableFieldNames = new Set(table.fields
|
|
1317
|
+
.filter((f) => !(0, utils_1.isRelationField)(f.name, table))
|
|
1318
|
+
.map((f) => f.name));
|
|
1319
|
+
for (const field of conditionType.inputFields) {
|
|
1320
|
+
if (tableFieldNames.has(field.name))
|
|
1321
|
+
continue;
|
|
1322
|
+
// Collect the base type name of this extra field
|
|
1323
|
+
const baseName = (0, type_resolver_1.getTypeBaseName)(field.type);
|
|
1324
|
+
if (baseName && !scalars_1.SCALAR_NAMES.has(baseName)) {
|
|
1325
|
+
extraTypes.add(baseName);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return extraTypes;
|
|
1330
|
+
}
|
|
1331
|
+
// ============================================================================
|
|
1246
1332
|
// Main Generator (AST-based)
|
|
1247
1333
|
// ============================================================================
|
|
1248
1334
|
/**
|
|
1249
1335
|
* Generate comprehensive input-types.ts file using Babel AST
|
|
1250
1336
|
*/
|
|
1251
|
-
function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloadTypes, comments = true) {
|
|
1337
|
+
function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloadTypes, comments = true, options) {
|
|
1338
|
+
const conditionEnabled = options?.condition !== false;
|
|
1252
1339
|
const statements = [];
|
|
1253
1340
|
const tablesList = tables ?? [];
|
|
1254
1341
|
const hasTables = tablesList.length > 0;
|
|
@@ -1273,9 +1360,15 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
|
|
|
1273
1360
|
// 4. Table filter types
|
|
1274
1361
|
statements.push(...generateTableFilterTypes(tablesList));
|
|
1275
1362
|
// 4b. Table condition types (simple equality filter)
|
|
1276
|
-
|
|
1363
|
+
// Pass typeRegistry to merge plugin-injected condition fields
|
|
1364
|
+
// (e.g., vectorEmbedding from VectorSearchPlugin)
|
|
1365
|
+
if (conditionEnabled) {
|
|
1366
|
+
statements.push(...generateTableConditionTypes(tablesList, typeRegistry));
|
|
1367
|
+
}
|
|
1277
1368
|
// 5. OrderBy types
|
|
1278
|
-
|
|
1369
|
+
// Pass typeRegistry to merge plugin-injected orderBy values
|
|
1370
|
+
// (e.g., EMBEDDING_DISTANCE_ASC/DESC from VectorSearchPlugin)
|
|
1371
|
+
statements.push(...generateOrderByTypes(tablesList, typeRegistry));
|
|
1279
1372
|
// 6. CRUD input types
|
|
1280
1373
|
statements.push(...generateAllCrudInputTypes(tablesList, typeRegistry));
|
|
1281
1374
|
}
|
|
@@ -1283,8 +1376,21 @@ function generateInputTypesFile(typeRegistry, usedInputTypes, tables, usedPayloa
|
|
|
1283
1376
|
// Always emit this export so generated model/custom-op imports stay valid.
|
|
1284
1377
|
statements.push(...generateConnectionFieldsMap(tablesList, tableByName));
|
|
1285
1378
|
// 7. Custom input types from TypeRegistry
|
|
1379
|
+
// Also include any extra types referenced by plugin-injected condition fields
|
|
1380
|
+
const mergedUsedInputTypes = new Set(usedInputTypes);
|
|
1381
|
+
if (hasTables && conditionEnabled) {
|
|
1382
|
+
const conditionExtraTypes = collectConditionExtraInputTypes(tablesList, typeRegistry);
|
|
1383
|
+
for (const typeName of conditionExtraTypes) {
|
|
1384
|
+
mergedUsedInputTypes.add(typeName);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1286
1387
|
const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined;
|
|
1287
|
-
|
|
1388
|
+
// Pass customScalarTypes + enumTypes as already-generated to avoid duplicate declarations
|
|
1389
|
+
const alreadyGenerated = new Set([
|
|
1390
|
+
...customScalarTypes,
|
|
1391
|
+
...enumTypes,
|
|
1392
|
+
]);
|
|
1393
|
+
statements.push(...generateCustomInputTypes(typeRegistry, mergedUsedInputTypes, tableCrudTypes, comments, alreadyGenerated));
|
|
1288
1394
|
// 8. Payload/return types for custom operations
|
|
1289
1395
|
if (usedPayloadTypes && usedPayloadTypes.size > 0) {
|
|
1290
1396
|
const alreadyGeneratedTypes = new Set();
|
|
@@ -5,5 +5,9 @@ export interface GeneratedModelFile {
|
|
|
5
5
|
modelName: string;
|
|
6
6
|
tableName: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function generateModelFile(table: CleanTable, _useSharedTypes: boolean
|
|
9
|
-
|
|
8
|
+
export declare function generateModelFile(table: CleanTable, _useSharedTypes: boolean, options?: {
|
|
9
|
+
condition?: boolean;
|
|
10
|
+
}): GeneratedModelFile;
|
|
11
|
+
export declare function generateAllModelFiles(tables: CleanTable[], useSharedTypes: boolean, options?: {
|
|
12
|
+
condition?: boolean;
|
|
13
|
+
}): GeneratedModelFile[];
|
|
@@ -102,7 +102,8 @@ function strictSelectGuard(selectTypeName) {
|
|
|
102
102
|
t.tsTypeReference(t.identifier(selectTypeName)),
|
|
103
103
|
]));
|
|
104
104
|
}
|
|
105
|
-
function generateModelFile(table, _useSharedTypes) {
|
|
105
|
+
function generateModelFile(table, _useSharedTypes, options) {
|
|
106
|
+
const conditionEnabled = options?.condition !== false;
|
|
106
107
|
const { typeName, singularName, pluralName } = (0, utils_1.getTableNames)(table);
|
|
107
108
|
const modelName = `${typeName}Model`;
|
|
108
109
|
const baseFileName = (0, utils_1.lcFirst)(typeName);
|
|
@@ -111,6 +112,7 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
111
112
|
const selectTypeName = `${typeName}Select`;
|
|
112
113
|
const relationTypeName = `${typeName}WithRelations`;
|
|
113
114
|
const whereTypeName = (0, utils_1.getFilterTypeName)(table);
|
|
115
|
+
const conditionTypeName = conditionEnabled ? `${typeName}Condition` : undefined;
|
|
114
116
|
const orderByTypeName = (0, utils_1.getOrderByTypeName)(table);
|
|
115
117
|
const createInputTypeName = `Create${typeName}Input`;
|
|
116
118
|
const updateInputTypeName = `Update${typeName}Input`;
|
|
@@ -145,16 +147,18 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
145
147
|
'InferSelectResult',
|
|
146
148
|
'StrictSelect',
|
|
147
149
|
], true));
|
|
148
|
-
|
|
150
|
+
const inputTypeImports = [
|
|
149
151
|
typeName,
|
|
150
152
|
relationTypeName,
|
|
151
153
|
selectTypeName,
|
|
152
154
|
whereTypeName,
|
|
155
|
+
...(conditionTypeName ? [conditionTypeName] : []),
|
|
153
156
|
orderByTypeName,
|
|
154
157
|
createInputTypeName,
|
|
155
158
|
updateInputTypeName,
|
|
156
159
|
patchTypeName,
|
|
157
|
-
]
|
|
160
|
+
];
|
|
161
|
+
statements.push(createImportDeclaration('../input-types', inputTypeImports, true));
|
|
158
162
|
statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap']));
|
|
159
163
|
const classBody = [];
|
|
160
164
|
// Constructor
|
|
@@ -168,11 +172,15 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
168
172
|
const pkTsType = () => tsTypeFromPrimitive(pkField.tsType);
|
|
169
173
|
// ── findMany ───────────────────────────────────────────────────────────
|
|
170
174
|
{
|
|
171
|
-
const
|
|
172
|
-
sel,
|
|
173
|
-
t.tsTypeReference(t.identifier(whereTypeName)),
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
const findManyTypeArgs = [
|
|
176
|
+
(sel) => sel,
|
|
177
|
+
() => t.tsTypeReference(t.identifier(whereTypeName)),
|
|
178
|
+
...(conditionTypeName
|
|
179
|
+
? [() => t.tsTypeReference(t.identifier(conditionTypeName))]
|
|
180
|
+
: []),
|
|
181
|
+
() => t.tsTypeReference(t.identifier(orderByTypeName)),
|
|
182
|
+
];
|
|
183
|
+
const argsType = (sel) => t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation(findManyTypeArgs.map(fn => fn(sel))));
|
|
176
184
|
const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
|
|
177
185
|
t.tsTypeLiteral([
|
|
178
186
|
t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('ConnectionResult'), t.tsTypeParameterInstantiation([
|
|
@@ -190,34 +198,47 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
190
198
|
strictSelectGuard(selectTypeName),
|
|
191
199
|
]));
|
|
192
200
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
201
|
+
const findManyObjProps = [
|
|
202
|
+
t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)),
|
|
203
|
+
...(conditionTypeName
|
|
204
|
+
? [
|
|
205
|
+
t.objectProperty(t.identifier('condition'), t.optionalMemberExpression(t.identifier('args'), t.identifier('condition'), false, true)),
|
|
206
|
+
]
|
|
207
|
+
: []),
|
|
208
|
+
t.objectProperty(t.identifier('orderBy'), t.tsAsExpression(t.optionalMemberExpression(t.identifier('args'), t.identifier('orderBy'), false, true), t.tsUnionType([
|
|
209
|
+
t.tsArrayType(t.tsStringKeyword()),
|
|
210
|
+
t.tsUndefinedKeyword(),
|
|
211
|
+
]))),
|
|
212
|
+
t.objectProperty(t.identifier('first'), t.optionalMemberExpression(t.identifier('args'), t.identifier('first'), false, true)),
|
|
213
|
+
t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)),
|
|
214
|
+
t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)),
|
|
215
|
+
t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)),
|
|
216
|
+
t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)),
|
|
217
|
+
];
|
|
193
218
|
const bodyArgs = [
|
|
194
219
|
t.stringLiteral(typeName),
|
|
195
220
|
t.stringLiteral(pluralQueryName),
|
|
196
221
|
selectExpr,
|
|
197
|
-
t.objectExpression(
|
|
198
|
-
t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)),
|
|
199
|
-
t.objectProperty(t.identifier('orderBy'), t.tsAsExpression(t.optionalMemberExpression(t.identifier('args'), t.identifier('orderBy'), false, true), t.tsUnionType([
|
|
200
|
-
t.tsArrayType(t.tsStringKeyword()),
|
|
201
|
-
t.tsUndefinedKeyword(),
|
|
202
|
-
]))),
|
|
203
|
-
t.objectProperty(t.identifier('first'), t.optionalMemberExpression(t.identifier('args'), t.identifier('first'), false, true)),
|
|
204
|
-
t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)),
|
|
205
|
-
t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)),
|
|
206
|
-
t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)),
|
|
207
|
-
t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)),
|
|
208
|
-
]),
|
|
222
|
+
t.objectExpression(findManyObjProps),
|
|
209
223
|
t.stringLiteral(whereTypeName),
|
|
210
224
|
t.stringLiteral(orderByTypeName),
|
|
211
225
|
t.identifier('connectionFieldsMap'),
|
|
226
|
+
...(conditionTypeName
|
|
227
|
+
? [t.stringLiteral(conditionTypeName)]
|
|
228
|
+
: []),
|
|
212
229
|
];
|
|
213
230
|
classBody.push(createClassMethod('findMany', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName)));
|
|
214
231
|
}
|
|
215
232
|
// ── findFirst ──────────────────────────────────────────────────────────
|
|
216
233
|
{
|
|
217
|
-
const
|
|
218
|
-
sel,
|
|
219
|
-
t.tsTypeReference(t.identifier(whereTypeName)),
|
|
220
|
-
|
|
234
|
+
const findFirstTypeArgs = [
|
|
235
|
+
(sel) => sel,
|
|
236
|
+
() => t.tsTypeReference(t.identifier(whereTypeName)),
|
|
237
|
+
...(conditionTypeName
|
|
238
|
+
? [() => t.tsTypeReference(t.identifier(conditionTypeName))]
|
|
239
|
+
: []),
|
|
240
|
+
];
|
|
241
|
+
const argsType = (sel) => t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation(findFirstTypeArgs.map(fn => fn(sel))));
|
|
221
242
|
const retType = (sel) => t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
|
|
222
243
|
t.tsTypeLiteral([
|
|
223
244
|
t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation(t.tsTypeLiteral([
|
|
@@ -235,15 +256,24 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
235
256
|
strictSelectGuard(selectTypeName),
|
|
236
257
|
]));
|
|
237
258
|
const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select'));
|
|
259
|
+
const findFirstObjProps = [
|
|
260
|
+
t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)),
|
|
261
|
+
...(conditionTypeName
|
|
262
|
+
? [
|
|
263
|
+
t.objectProperty(t.identifier('condition'), t.optionalMemberExpression(t.identifier('args'), t.identifier('condition'), false, true)),
|
|
264
|
+
]
|
|
265
|
+
: []),
|
|
266
|
+
];
|
|
238
267
|
const bodyArgs = [
|
|
239
268
|
t.stringLiteral(typeName),
|
|
240
269
|
t.stringLiteral(pluralQueryName),
|
|
241
270
|
selectExpr,
|
|
242
|
-
t.objectExpression(
|
|
243
|
-
t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)),
|
|
244
|
-
]),
|
|
271
|
+
t.objectExpression(findFirstObjProps),
|
|
245
272
|
t.stringLiteral(whereTypeName),
|
|
246
273
|
t.identifier('connectionFieldsMap'),
|
|
274
|
+
...(conditionTypeName
|
|
275
|
+
? [t.stringLiteral(conditionTypeName)]
|
|
276
|
+
: []),
|
|
247
277
|
];
|
|
248
278
|
classBody.push(createClassMethod('findFirst', createTypeParam(selectTypeName), [implParam], retType(sRef()), buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName)));
|
|
249
279
|
}
|
|
@@ -447,6 +477,6 @@ function generateModelFile(table, _useSharedTypes) {
|
|
|
447
477
|
tableName: table.name,
|
|
448
478
|
};
|
|
449
479
|
}
|
|
450
|
-
function generateAllModelFiles(tables, useSharedTypes) {
|
|
451
|
-
return tables.map((table) => generateModelFile(table, useSharedTypes));
|
|
480
|
+
function generateAllModelFiles(tables, useSharedTypes, options) {
|
|
481
|
+
return tables.map((table) => generateModelFile(table, useSharedTypes, options));
|
|
452
482
|
}
|
|
@@ -150,9 +150,10 @@ export interface PageInfo {
|
|
|
150
150
|
/**
|
|
151
151
|
* Arguments for findMany operations
|
|
152
152
|
*/
|
|
153
|
-
export interface FindManyArgs<TSelect, TWhere, TOrderBy> {
|
|
153
|
+
export interface FindManyArgs<TSelect, TWhere, TCondition, TOrderBy> {
|
|
154
154
|
select?: TSelect;
|
|
155
155
|
where?: TWhere;
|
|
156
|
+
condition?: TCondition;
|
|
156
157
|
orderBy?: TOrderBy[];
|
|
157
158
|
first?: number;
|
|
158
159
|
last?: number;
|
|
@@ -163,9 +164,10 @@ export interface FindManyArgs<TSelect, TWhere, TOrderBy> {
|
|
|
163
164
|
/**
|
|
164
165
|
* Arguments for findFirst/findUnique operations
|
|
165
166
|
*/
|
|
166
|
-
export interface FindFirstArgs<TSelect, TWhere> {
|
|
167
|
+
export interface FindFirstArgs<TSelect, TWhere, TCondition> {
|
|
167
168
|
select?: TSelect;
|
|
168
169
|
where?: TWhere;
|
|
170
|
+
condition?: TCondition;
|
|
169
171
|
}
|
|
170
172
|
/**
|
|
171
173
|
* Arguments for create operations
|
package/core/codegen/scalars.js
CHANGED
|
@@ -38,6 +38,8 @@ exports.SCALAR_TS_MAP = {
|
|
|
38
38
|
MacAddr: 'string',
|
|
39
39
|
TsVector: 'string',
|
|
40
40
|
TsQuery: 'string',
|
|
41
|
+
// Vector types (pgvector) — serialized as [Float] in GraphQL
|
|
42
|
+
Vector: 'number[]',
|
|
41
43
|
// File upload
|
|
42
44
|
Upload: 'File',
|
|
43
45
|
};
|
|
@@ -56,6 +58,12 @@ exports.SCALAR_FILTER_MAP = {
|
|
|
56
58
|
BigFloat: 'BigFloatFilter',
|
|
57
59
|
BitString: 'BitStringFilter',
|
|
58
60
|
InternetAddress: 'InternetAddressFilter',
|
|
61
|
+
// VectorFilter provides equality/distinct operators (isNull, equalTo, etc.) for vector
|
|
62
|
+
// columns on Filter types. While similarity search is done via condition types
|
|
63
|
+
// (e.g., embeddingNearby on ContactCondition), postgraphile-plugin-connection-filter
|
|
64
|
+
// may still auto-generate a filter type for vector columns. Without this mapping,
|
|
65
|
+
// those fields would be silently omitted from the generated SDK.
|
|
66
|
+
Vector: 'VectorFilter',
|
|
59
67
|
FullText: 'FullTextFilter',
|
|
60
68
|
Interval: 'StringFilter',
|
|
61
69
|
};
|
|
@@ -17,9 +17,9 @@ if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
|
17
17
|
process.exit(0);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// Check for --tty false to enable non-interactive mode (noTty)
|
|
20
|
+
// Check for --tty false or --no-tty to enable non-interactive mode (noTty)
|
|
21
21
|
const ttyIdx = process.argv.indexOf('--tty');
|
|
22
|
-
const noTty = ttyIdx !== -1 && process.argv[ttyIdx + 1] === 'false';
|
|
22
|
+
const noTty = (ttyIdx !== -1 && process.argv[ttyIdx + 1] === 'false') || process.argv.includes('--no-tty');
|
|
23
23
|
|
|
24
24
|
const options: Partial<CLIOptions> = {
|
|
25
25
|
noTty,
|
|
@@ -145,6 +145,34 @@ export function parseMutationInput(
|
|
|
145
145
|
return answers;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Reconstruct nested objects from dot-notation CLI answers.
|
|
150
|
+
* When INPUT_OBJECT args are flattened to dot-notation questions
|
|
151
|
+
* (e.g. `--input.email foo --input.password bar`), this function
|
|
152
|
+
* rebuilds the nested structure expected by the ORM:
|
|
153
|
+
*
|
|
154
|
+
* { 'input.email': 'foo', 'input.password': 'bar' }
|
|
155
|
+
* → { input: { email: 'foo', password: 'bar' } }
|
|
156
|
+
*
|
|
157
|
+
* Non-dotted keys are passed through unchanged.
|
|
158
|
+
* Uses `nested-obj` for safe nested property setting.
|
|
159
|
+
*/
|
|
160
|
+
export function unflattenDotNotation(
|
|
161
|
+
answers: Record<string, unknown>,
|
|
162
|
+
): Record<string, unknown> {
|
|
163
|
+
const result: Record<string, unknown> = {};
|
|
164
|
+
|
|
165
|
+
for (const [key, value] of Object.entries(answers)) {
|
|
166
|
+
if (key.includes('.')) {
|
|
167
|
+
objectPath.set(result, key, value);
|
|
168
|
+
} else {
|
|
169
|
+
result[key] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
148
176
|
/**
|
|
149
177
|
* Build a select object from a comma-separated list of dot-notation paths.
|
|
150
178
|
* Uses `nested-obj` to parse paths like 'clientMutationId,result.accessToken,result.userId'
|
|
@@ -201,12 +201,13 @@ export function buildSelections(
|
|
|
201
201
|
// Document Builders
|
|
202
202
|
// ============================================================================
|
|
203
203
|
|
|
204
|
-
export function buildFindManyDocument<TSelect, TWhere>(
|
|
204
|
+
export function buildFindManyDocument<TSelect, TWhere, TCondition = never>(
|
|
205
205
|
operationName: string,
|
|
206
206
|
queryField: string,
|
|
207
207
|
select: TSelect,
|
|
208
208
|
args: {
|
|
209
209
|
where?: TWhere;
|
|
210
|
+
condition?: TCondition;
|
|
210
211
|
orderBy?: string[];
|
|
211
212
|
first?: number;
|
|
212
213
|
last?: number;
|
|
@@ -217,6 +218,7 @@ export function buildFindManyDocument<TSelect, TWhere>(
|
|
|
217
218
|
filterTypeName: string,
|
|
218
219
|
orderByTypeName: string,
|
|
219
220
|
connectionFieldsMap?: Record<string, Record<string, string>>,
|
|
221
|
+
conditionTypeName?: string,
|
|
220
222
|
): { document: string; variables: Record<string, unknown> } {
|
|
221
223
|
const selections = select
|
|
222
224
|
? buildSelections(
|
|
@@ -230,6 +232,16 @@ export function buildFindManyDocument<TSelect, TWhere>(
|
|
|
230
232
|
const queryArgs: ArgumentNode[] = [];
|
|
231
233
|
const variables: Record<string, unknown> = {};
|
|
232
234
|
|
|
235
|
+
addVariable(
|
|
236
|
+
{
|
|
237
|
+
varName: 'condition',
|
|
238
|
+
typeName: conditionTypeName,
|
|
239
|
+
value: args.condition,
|
|
240
|
+
},
|
|
241
|
+
variableDefinitions,
|
|
242
|
+
queryArgs,
|
|
243
|
+
variables,
|
|
244
|
+
);
|
|
233
245
|
addVariable(
|
|
234
246
|
{
|
|
235
247
|
varName: 'where',
|
|
@@ -308,13 +320,14 @@ export function buildFindManyDocument<TSelect, TWhere>(
|
|
|
308
320
|
return { document: print(document), variables };
|
|
309
321
|
}
|
|
310
322
|
|
|
311
|
-
export function buildFindFirstDocument<TSelect, TWhere>(
|
|
323
|
+
export function buildFindFirstDocument<TSelect, TWhere, TCondition = never>(
|
|
312
324
|
operationName: string,
|
|
313
325
|
queryField: string,
|
|
314
326
|
select: TSelect,
|
|
315
|
-
args: { where?: TWhere },
|
|
327
|
+
args: { where?: TWhere; condition?: TCondition },
|
|
316
328
|
filterTypeName: string,
|
|
317
329
|
connectionFieldsMap?: Record<string, Record<string, string>>,
|
|
330
|
+
conditionTypeName?: string,
|
|
318
331
|
): { document: string; variables: Record<string, unknown> } {
|
|
319
332
|
const selections = select
|
|
320
333
|
? buildSelections(
|
|
@@ -335,6 +348,16 @@ export function buildFindFirstDocument<TSelect, TWhere>(
|
|
|
335
348
|
queryArgs,
|
|
336
349
|
variables,
|
|
337
350
|
);
|
|
351
|
+
addVariable(
|
|
352
|
+
{
|
|
353
|
+
varName: 'condition',
|
|
354
|
+
typeName: conditionTypeName,
|
|
355
|
+
value: args.condition,
|
|
356
|
+
},
|
|
357
|
+
variableDefinitions,
|
|
358
|
+
queryArgs,
|
|
359
|
+
variables,
|
|
360
|
+
);
|
|
338
361
|
addVariable(
|
|
339
362
|
{
|
|
340
363
|
varName: 'where',
|
|
@@ -799,7 +822,7 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] {
|
|
|
799
822
|
interface VariableSpec {
|
|
800
823
|
varName: string;
|
|
801
824
|
argName?: string;
|
|
802
|
-
typeName
|
|
825
|
+
typeName?: string;
|
|
803
826
|
value: unknown;
|
|
804
827
|
}
|
|
805
828
|
|
|
@@ -850,7 +873,7 @@ function addVariable(
|
|
|
850
873
|
args: ArgumentNode[],
|
|
851
874
|
variables: Record<string, unknown>,
|
|
852
875
|
): void {
|
|
853
|
-
if (spec.value === undefined) return;
|
|
876
|
+
if (spec.value === undefined || !spec.typeName) return;
|
|
854
877
|
|
|
855
878
|
definitions.push(
|
|
856
879
|
t.variableDefinition({
|
|
@@ -21,9 +21,10 @@ export interface PageInfo {
|
|
|
21
21
|
endCursor?: string | null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export interface FindManyArgs<TSelect, TWhere, TOrderBy> {
|
|
24
|
+
export interface FindManyArgs<TSelect, TWhere, TCondition = never, TOrderBy = never> {
|
|
25
25
|
select?: TSelect;
|
|
26
26
|
where?: TWhere;
|
|
27
|
+
condition?: TCondition;
|
|
27
28
|
orderBy?: TOrderBy[];
|
|
28
29
|
first?: number;
|
|
29
30
|
last?: number;
|
|
@@ -32,9 +33,10 @@ export interface FindManyArgs<TSelect, TWhere, TOrderBy> {
|
|
|
32
33
|
offset?: number;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export interface FindFirstArgs<TSelect, TWhere> {
|
|
36
|
+
export interface FindFirstArgs<TSelect, TWhere, TCondition = never> {
|
|
36
37
|
select?: TSelect;
|
|
37
38
|
where?: TWhere;
|
|
39
|
+
condition?: TCondition;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export interface CreateArgs<TSelect, TData> {
|
package/core/generate.js
CHANGED
|
@@ -316,18 +316,18 @@ async function generate(options = {}, internalOptions) {
|
|
|
316
316
|
? config.cli.toolName
|
|
317
317
|
: 'app';
|
|
318
318
|
if (docsConfig.readme) {
|
|
319
|
-
const readme = (0, docs_generator_1.generateReadme)(tables, allCustomOps, toolName);
|
|
319
|
+
const readme = (0, docs_generator_1.generateReadme)(tables, allCustomOps, toolName, customOperations.typeRegistry);
|
|
320
320
|
filesToWrite.push({ path: node_path_1.default.posix.join('cli', readme.fileName), content: readme.content });
|
|
321
321
|
}
|
|
322
322
|
if (docsConfig.agents) {
|
|
323
|
-
const agents = (0, docs_generator_1.generateAgentsDocs)(tables, allCustomOps, toolName);
|
|
323
|
+
const agents = (0, docs_generator_1.generateAgentsDocs)(tables, allCustomOps, toolName, customOperations.typeRegistry);
|
|
324
324
|
filesToWrite.push({ path: node_path_1.default.posix.join('cli', agents.fileName), content: agents.content });
|
|
325
325
|
}
|
|
326
326
|
if (docsConfig.mcp) {
|
|
327
|
-
allMcpTools.push(...(0, docs_generator_1.getCliMcpTools)(tables, allCustomOps, toolName));
|
|
327
|
+
allMcpTools.push(...(0, docs_generator_1.getCliMcpTools)(tables, allCustomOps, toolName, customOperations.typeRegistry));
|
|
328
328
|
}
|
|
329
329
|
if (docsConfig.skills) {
|
|
330
|
-
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName, targetName)) {
|
|
330
|
+
for (const skill of (0, docs_generator_1.generateSkills)(tables, allCustomOps, toolName, targetName, customOperations.typeRegistry)) {
|
|
331
331
|
skillsToWrite.push({ path: skill.fileName, content: skill.content });
|
|
332
332
|
}
|
|
333
333
|
}
|
|
@@ -616,9 +616,19 @@ async function generateMulti(options) {
|
|
|
616
616
|
const docsConfig = (0, docs_utils_1.resolveDocsConfig)(firstTargetDocsConfig);
|
|
617
617
|
const { resolveBuiltinNames } = await Promise.resolve().then(() => __importStar(require('./codegen/cli')));
|
|
618
618
|
const builtinNames = resolveBuiltinNames(cliTargets.map((t) => t.name), cliConfig.builtinNames);
|
|
619
|
+
// Merge all target type registries into a combined registry for docs generation
|
|
620
|
+
const combinedRegistry = new Map();
|
|
621
|
+
for (const t of cliTargets) {
|
|
622
|
+
if (t.typeRegistry) {
|
|
623
|
+
for (const [key, value] of t.typeRegistry) {
|
|
624
|
+
combinedRegistry.set(key, value);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
619
628
|
const docsInput = {
|
|
620
629
|
toolName,
|
|
621
630
|
builtinNames,
|
|
631
|
+
registry: combinedRegistry.size > 0 ? combinedRegistry : undefined,
|
|
622
632
|
targets: cliTargets.map((t) => ({
|
|
623
633
|
name: t.name,
|
|
624
634
|
endpoint: t.endpoint,
|