@constructive-io/graphql-codegen 3.3.2 → 4.0.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/README.md +0 -4
- package/core/ast.js +6 -5
- package/core/codegen/custom-mutations.js +22 -22
- package/core/codegen/custom-queries.js +24 -17
- package/core/codegen/hooks-ast.d.ts +1 -1
- package/core/codegen/hooks-ast.js +22 -5
- package/core/codegen/index.d.ts +1 -1
- package/core/codegen/mutations.js +16 -12
- package/core/codegen/orm/custom-ops-generator.js +37 -3
- package/core/codegen/orm/input-types-generator.js +161 -89
- package/core/codegen/orm/model-generator.js +72 -73
- package/core/codegen/orm/select-types.d.ts +27 -17
- package/core/codegen/queries.js +37 -25
- package/core/codegen/schema-types-generator.js +21 -0
- package/core/codegen/templates/hooks-selection.ts +12 -0
- package/core/codegen/templates/query-builder.ts +103 -59
- package/core/codegen/templates/select-types.ts +59 -33
- package/core/codegen/types.js +26 -0
- package/core/codegen/utils.d.ts +1 -1
- package/core/codegen/utils.js +1 -1
- package/core/custom-ast.js +9 -8
- package/core/database/index.js +2 -3
- package/core/index.d.ts +2 -0
- package/core/index.js +2 -0
- package/core/introspect/infer-tables.js +144 -58
- package/core/introspect/transform-schema.d.ts +1 -1
- package/core/introspect/transform-schema.js +3 -1
- package/esm/core/ast.js +6 -5
- package/esm/core/codegen/custom-mutations.js +23 -23
- package/esm/core/codegen/custom-queries.js +25 -18
- package/esm/core/codegen/hooks-ast.d.ts +1 -1
- package/esm/core/codegen/hooks-ast.js +22 -5
- package/esm/core/codegen/index.d.ts +1 -1
- package/esm/core/codegen/mutations.js +16 -12
- package/esm/core/codegen/orm/custom-ops-generator.js +38 -4
- package/esm/core/codegen/orm/input-types-generator.js +163 -91
- package/esm/core/codegen/orm/model-generator.js +73 -74
- package/esm/core/codegen/orm/select-types.d.ts +27 -17
- package/esm/core/codegen/queries.js +37 -25
- package/esm/core/codegen/schema-types-generator.js +21 -0
- package/esm/core/codegen/types.js +26 -0
- package/esm/core/codegen/utils.d.ts +1 -1
- package/esm/core/codegen/utils.js +1 -1
- package/esm/core/custom-ast.js +9 -8
- package/esm/core/database/index.js +2 -3
- package/esm/core/index.d.ts +2 -0
- package/esm/core/index.js +2 -0
- package/esm/core/introspect/infer-tables.js +144 -58
- package/esm/core/introspect/transform-schema.d.ts +1 -1
- package/esm/core/introspect/transform-schema.js +3 -1
- package/esm/generators/field-selector.js +1 -0
- package/esm/generators/index.d.ts +3 -0
- package/esm/generators/index.js +3 -0
- package/esm/generators/mutations.js +4 -4
- package/esm/generators/select.js +4 -4
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/types/schema.d.ts +5 -3
- package/generators/field-selector.js +1 -0
- package/generators/index.d.ts +3 -0
- package/generators/index.js +3 -0
- package/generators/mutations.js +3 -3
- package/generators/select.js +3 -3
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +10 -10
- package/types/schema.d.ts +5 -3
|
@@ -19,11 +19,10 @@ export async function buildSchemaFromDatabase(options) {
|
|
|
19
19
|
const { database, schemas, outDir, filename = 'schema.graphql' } = options;
|
|
20
20
|
// Ensure output directory exists
|
|
21
21
|
await fs.promises.mkdir(outDir, { recursive: true });
|
|
22
|
-
// Build schema SDL from database
|
|
22
|
+
// Build schema SDL from database (PostGraphile v5 preset-driven settings)
|
|
23
23
|
const sdl = await buildSchemaSDL({
|
|
24
24
|
database,
|
|
25
25
|
schemas,
|
|
26
|
-
graphile: { pgSettings: async () => ({ role: 'administrator' }) },
|
|
27
26
|
});
|
|
28
27
|
// Write schema to file
|
|
29
28
|
const schemaPath = path.join(outDir, filename);
|
|
@@ -40,9 +39,9 @@ export async function buildSchemaFromDatabase(options) {
|
|
|
40
39
|
*/
|
|
41
40
|
export async function buildSchemaSDLFromDatabase(options) {
|
|
42
41
|
const { database, schemas } = options;
|
|
42
|
+
// PostGraphile v5 resolves role/settings via preset configuration.
|
|
43
43
|
return buildSchemaSDL({
|
|
44
44
|
database,
|
|
45
45
|
schemas,
|
|
46
|
-
graphile: { pgSettings: async () => ({ role: 'administrator' }) },
|
|
47
46
|
});
|
|
48
47
|
}
|
package/esm/core/index.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ export { generate } from './generate';
|
|
|
9
9
|
export * from './types';
|
|
10
10
|
export * from './ast';
|
|
11
11
|
export * from './custom-ast';
|
|
12
|
+
/** @deprecated Legacy v4 query builder — use v5 ORM codegen instead */
|
|
12
13
|
export { MetaObject, QueryBuilder } from './query-builder';
|
|
14
|
+
/** @deprecated Legacy v4 meta-object utilities — v5 uses standard introspection */
|
|
13
15
|
export { convertFromMetaSchema, validateMetaObject } from './meta-object';
|
|
14
16
|
export * from './config';
|
|
15
17
|
export * from './codegen';
|
package/esm/core/index.js
CHANGED
|
@@ -11,8 +11,10 @@ export * from './types';
|
|
|
11
11
|
export * from './ast';
|
|
12
12
|
export * from './custom-ast';
|
|
13
13
|
// Query builder
|
|
14
|
+
/** @deprecated Legacy v4 query builder — use v5 ORM codegen instead */
|
|
14
15
|
export { MetaObject, QueryBuilder } from './query-builder';
|
|
15
16
|
// Meta object utilities
|
|
17
|
+
/** @deprecated Legacy v4 meta-object utilities — v5 uses standard introspection */
|
|
16
18
|
export { convertFromMetaSchema, validateMetaObject } from './meta-object';
|
|
17
19
|
// Configuration loading and resolution
|
|
18
20
|
export * from './config';
|
|
@@ -83,20 +83,19 @@ export function inferTablesFromIntrospection(introspection, options = {}) {
|
|
|
83
83
|
const { types, queryType, mutationType } = schema;
|
|
84
84
|
// Build lookup maps for efficient access
|
|
85
85
|
const typeMap = buildTypeMap(types);
|
|
86
|
+
const { entityNames, entityToConnection, connectionToEntity } = buildEntityConnectionMaps(types, typeMap);
|
|
86
87
|
const queryFields = getTypeFields(typeMap.get(queryType.name));
|
|
87
88
|
const mutationFields = mutationType
|
|
88
89
|
? getTypeFields(typeMap.get(mutationType.name))
|
|
89
90
|
: [];
|
|
90
|
-
// Step 1:
|
|
91
|
-
const entityNames = detectEntityTypes(types);
|
|
92
|
-
// Step 2: Build CleanTable for each entity
|
|
91
|
+
// Step 1: Build CleanTable for each inferred entity
|
|
93
92
|
const tables = [];
|
|
94
93
|
for (const entityName of entityNames) {
|
|
95
94
|
const entityType = typeMap.get(entityName);
|
|
96
95
|
if (!entityType)
|
|
97
96
|
continue;
|
|
98
97
|
// Infer all metadata for this entity
|
|
99
|
-
const { table, hasRealOperation } = buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields);
|
|
98
|
+
const { table, hasRealOperation } = buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields, entityToConnection, connectionToEntity);
|
|
100
99
|
// Only include tables that have at least one real operation
|
|
101
100
|
if (hasRealOperation) {
|
|
102
101
|
tables.push(table);
|
|
@@ -104,17 +103,17 @@ export function inferTablesFromIntrospection(introspection, options = {}) {
|
|
|
104
103
|
}
|
|
105
104
|
return tables;
|
|
106
105
|
}
|
|
107
|
-
// ============================================================================
|
|
108
|
-
// Entity Detection
|
|
109
|
-
// ============================================================================
|
|
110
106
|
/**
|
|
111
|
-
*
|
|
107
|
+
* Infer entity <-> connection mappings from schema types.
|
|
112
108
|
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
109
|
+
* Prefer deriving entity names from the `nodes` field of connection types.
|
|
110
|
+
* This is robust across v4/v5 naming variations (e.g. `UsersConnection` and
|
|
111
|
+
* `UserConnection`).
|
|
115
112
|
*/
|
|
116
|
-
function
|
|
113
|
+
function buildEntityConnectionMaps(types, typeMap) {
|
|
117
114
|
const entityNames = new Set();
|
|
115
|
+
const entityToConnection = new Map();
|
|
116
|
+
const connectionToEntity = new Map();
|
|
118
117
|
const typeNames = new Set(types.map((t) => t.name));
|
|
119
118
|
for (const type of types) {
|
|
120
119
|
// Skip internal types
|
|
@@ -125,26 +124,58 @@ function detectEntityTypes(types) {
|
|
|
125
124
|
// Check for Connection pattern
|
|
126
125
|
const connectionMatch = type.name.match(PATTERNS.connection);
|
|
127
126
|
if (connectionMatch) {
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
// Verify the entity type actually exists
|
|
131
|
-
if (typeNames.has(
|
|
132
|
-
|
|
127
|
+
const fallbackEntityName = singularize(connectionMatch[1]);
|
|
128
|
+
const entityName = resolveEntityNameFromConnectionType(type, typeMap) ?? fallbackEntityName;
|
|
129
|
+
// Verify the entity type actually exists and is not a built-in/internal type.
|
|
130
|
+
if (typeNames.has(entityName) &&
|
|
131
|
+
!BUILTIN_TYPES.has(entityName) &&
|
|
132
|
+
!isInternalType(entityName)) {
|
|
133
|
+
entityNames.add(entityName);
|
|
134
|
+
entityToConnection.set(entityName, type.name);
|
|
135
|
+
connectionToEntity.set(type.name, entityName);
|
|
133
136
|
}
|
|
134
137
|
}
|
|
135
138
|
}
|
|
136
|
-
return
|
|
139
|
+
return {
|
|
140
|
+
entityNames,
|
|
141
|
+
entityToConnection,
|
|
142
|
+
connectionToEntity,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Attempt to resolve an entity name from a connection type by inspecting its
|
|
147
|
+
* `nodes` field.
|
|
148
|
+
*/
|
|
149
|
+
function resolveEntityNameFromConnectionType(connectionType, typeMap) {
|
|
150
|
+
if (!connectionType.fields)
|
|
151
|
+
return null;
|
|
152
|
+
const nodesField = connectionType.fields.find((field) => field.name === 'nodes');
|
|
153
|
+
if (!nodesField)
|
|
154
|
+
return null;
|
|
155
|
+
const nodeTypeName = getBaseTypeName(nodesField.type);
|
|
156
|
+
if (!nodeTypeName)
|
|
157
|
+
return null;
|
|
158
|
+
const nodeType = typeMap.get(nodeTypeName);
|
|
159
|
+
if (!nodeType || nodeType.kind !== 'OBJECT')
|
|
160
|
+
return null;
|
|
161
|
+
if (nodeTypeName.endsWith('Connection'))
|
|
162
|
+
return null;
|
|
163
|
+
if (BUILTIN_TYPES.has(nodeTypeName))
|
|
164
|
+
return null;
|
|
165
|
+
if (isInternalType(nodeTypeName))
|
|
166
|
+
return null;
|
|
167
|
+
return nodeTypeName;
|
|
137
168
|
}
|
|
138
169
|
/**
|
|
139
170
|
* Build a complete CleanTable from an entity type
|
|
140
171
|
*/
|
|
141
|
-
function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields) {
|
|
172
|
+
function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields, entityToConnection, connectionToEntity) {
|
|
142
173
|
// Extract scalar fields from entity type
|
|
143
|
-
const fields = extractEntityFields(entityType, typeMap);
|
|
174
|
+
const fields = extractEntityFields(entityType, typeMap, entityToConnection);
|
|
144
175
|
// Infer relations from entity fields
|
|
145
|
-
const relations = inferRelations(entityType,
|
|
176
|
+
const relations = inferRelations(entityType, entityToConnection, connectionToEntity);
|
|
146
177
|
// Match query and mutation operations
|
|
147
|
-
const queryOps = matchQueryOperations(entityName, queryFields,
|
|
178
|
+
const queryOps = matchQueryOperations(entityName, queryFields, entityToConnection);
|
|
148
179
|
const mutationOps = matchMutationOperations(entityName, mutationFields);
|
|
149
180
|
// Check if we found at least one real operation (not a fallback)
|
|
150
181
|
const hasRealOperation = !!(queryOps.all ||
|
|
@@ -154,16 +185,19 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
|
|
|
154
185
|
mutationOps.delete);
|
|
155
186
|
// Infer primary key from mutation inputs
|
|
156
187
|
const constraints = inferConstraints(entityName, typeMap);
|
|
188
|
+
// Infer the patch field name from UpdateXxxInput (e.g., "userPatch")
|
|
189
|
+
const patchFieldName = inferPatchFieldName(entityName, typeMap);
|
|
157
190
|
// Build inflection map from discovered types
|
|
158
|
-
const inflection = buildInflection(entityName, typeMap);
|
|
191
|
+
const inflection = buildInflection(entityName, typeMap, entityToConnection);
|
|
159
192
|
// Combine query operations with fallbacks for UI purposes
|
|
160
193
|
// (but hasRealOperation indicates if we should include this table)
|
|
161
194
|
const query = {
|
|
162
195
|
all: queryOps.all ?? lcFirst(pluralize(entityName)),
|
|
163
|
-
one: queryOps.one
|
|
196
|
+
one: queryOps.one,
|
|
164
197
|
create: mutationOps.create ?? `create${entityName}`,
|
|
165
198
|
update: mutationOps.update,
|
|
166
199
|
delete: mutationOps.delete,
|
|
200
|
+
patchFieldName,
|
|
167
201
|
};
|
|
168
202
|
return {
|
|
169
203
|
table: {
|
|
@@ -184,7 +218,7 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
|
|
|
184
218
|
* Extract scalar fields from an entity type
|
|
185
219
|
* Excludes relation fields (those returning other entity types or connections)
|
|
186
220
|
*/
|
|
187
|
-
function extractEntityFields(entityType, typeMap) {
|
|
221
|
+
function extractEntityFields(entityType, typeMap, entityToConnection) {
|
|
188
222
|
const fields = [];
|
|
189
223
|
if (!entityType.fields)
|
|
190
224
|
return fields;
|
|
@@ -197,7 +231,7 @@ function extractEntityFields(entityType, typeMap) {
|
|
|
197
231
|
if (fieldType?.kind === 'OBJECT') {
|
|
198
232
|
// Check if it's a Connection type (hasMany) or entity type (belongsTo)
|
|
199
233
|
if (baseTypeName.endsWith('Connection') ||
|
|
200
|
-
isEntityType(baseTypeName,
|
|
234
|
+
isEntityType(baseTypeName, entityToConnection)) {
|
|
201
235
|
continue; // Skip relation fields
|
|
202
236
|
}
|
|
203
237
|
}
|
|
@@ -212,9 +246,8 @@ function extractEntityFields(entityType, typeMap) {
|
|
|
212
246
|
/**
|
|
213
247
|
* Check if a type name is an entity type (has a corresponding Connection)
|
|
214
248
|
*/
|
|
215
|
-
function isEntityType(typeName,
|
|
216
|
-
|
|
217
|
-
return typeMap.has(connectionName);
|
|
249
|
+
function isEntityType(typeName, entityToConnection) {
|
|
250
|
+
return entityToConnection.has(typeName);
|
|
218
251
|
}
|
|
219
252
|
/**
|
|
220
253
|
* Convert IntrospectionTypeRef to CleanFieldType
|
|
@@ -235,7 +268,7 @@ function convertToCleanFieldType(typeRef) {
|
|
|
235
268
|
/**
|
|
236
269
|
* Infer relations from entity type fields
|
|
237
270
|
*/
|
|
238
|
-
function inferRelations(entityType,
|
|
271
|
+
function inferRelations(entityType, entityToConnection, connectionToEntity) {
|
|
239
272
|
const belongsTo = [];
|
|
240
273
|
const hasMany = [];
|
|
241
274
|
const manyToMany = [];
|
|
@@ -248,17 +281,17 @@ function inferRelations(entityType, typeMap) {
|
|
|
248
281
|
continue;
|
|
249
282
|
// Check for Connection type → hasMany or manyToMany
|
|
250
283
|
if (baseTypeName.endsWith('Connection')) {
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
manyToMany.push(
|
|
284
|
+
const resolvedRelation = inferHasManyOrManyToMany(field, baseTypeName, connectionToEntity);
|
|
285
|
+
if (resolvedRelation.type === 'manyToMany') {
|
|
286
|
+
manyToMany.push(resolvedRelation.relation);
|
|
254
287
|
}
|
|
255
288
|
else {
|
|
256
|
-
hasMany.push(
|
|
289
|
+
hasMany.push(resolvedRelation.relation);
|
|
257
290
|
}
|
|
258
291
|
continue;
|
|
259
292
|
}
|
|
260
293
|
// Check for entity type → belongsTo
|
|
261
|
-
if (isEntityType(baseTypeName,
|
|
294
|
+
if (isEntityType(baseTypeName, entityToConnection)) {
|
|
262
295
|
belongsTo.push({
|
|
263
296
|
fieldName: field.name,
|
|
264
297
|
isUnique: false, // Can't determine from introspection alone
|
|
@@ -276,11 +309,13 @@ function inferRelations(entityType, typeMap) {
|
|
|
276
309
|
* ManyToMany pattern: field name contains "By" and "And"
|
|
277
310
|
* e.g., "productsByOrderItemOrderIdAndProductId"
|
|
278
311
|
*/
|
|
279
|
-
function inferHasManyOrManyToMany(field, connectionTypeName,
|
|
280
|
-
//
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
312
|
+
function inferHasManyOrManyToMany(field, connectionTypeName, connectionToEntity) {
|
|
313
|
+
// Resolve the related entity from discovered connection mappings first.
|
|
314
|
+
const relatedEntityName = connectionToEntity.get(connectionTypeName) ?? (() => {
|
|
315
|
+
const match = connectionTypeName.match(PATTERNS.connection);
|
|
316
|
+
const relatedPluralName = match ? match[1] : connectionTypeName;
|
|
317
|
+
return singularize(relatedPluralName);
|
|
318
|
+
})();
|
|
284
319
|
// Check for manyToMany pattern in field name
|
|
285
320
|
const isManyToMany = field.name.includes('By') && field.name.includes('And');
|
|
286
321
|
if (isManyToMany) {
|
|
@@ -326,9 +361,9 @@ function inferHasManyOrManyToMany(field, connectionTypeName, typeMap) {
|
|
|
326
361
|
* - List query: returns {PluralName}Connection (e.g., users → UsersConnection)
|
|
327
362
|
* - Single query: returns {EntityName} with id/nodeId arg (e.g., user → User)
|
|
328
363
|
*/
|
|
329
|
-
function matchQueryOperations(entityName, queryFields,
|
|
364
|
+
function matchQueryOperations(entityName, queryFields, entityToConnection) {
|
|
330
365
|
const pluralName = pluralize(entityName);
|
|
331
|
-
const connectionTypeName = `${pluralName}Connection`;
|
|
366
|
+
const connectionTypeName = entityToConnection.get(entityName) ?? `${pluralName}Connection`;
|
|
332
367
|
let all = null;
|
|
333
368
|
let one = null;
|
|
334
369
|
for (const field of queryFields) {
|
|
@@ -412,21 +447,18 @@ function inferConstraints(entityName, typeMap) {
|
|
|
412
447
|
const deleteInputName = `Delete${entityName}Input`;
|
|
413
448
|
const updateInput = typeMap.get(updateInputName);
|
|
414
449
|
const deleteInput = typeMap.get(deleteInputName);
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
],
|
|
428
|
-
});
|
|
429
|
-
}
|
|
450
|
+
const keyInputField = inferPrimaryKeyFromInputObject(updateInput) ||
|
|
451
|
+
inferPrimaryKeyFromInputObject(deleteInput);
|
|
452
|
+
if (keyInputField) {
|
|
453
|
+
primaryKey.push({
|
|
454
|
+
name: 'primary',
|
|
455
|
+
fields: [
|
|
456
|
+
{
|
|
457
|
+
name: keyInputField.name,
|
|
458
|
+
type: convertToCleanFieldType(keyInputField.type),
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
});
|
|
430
462
|
}
|
|
431
463
|
// If no PK found from inputs, try to find 'id' field in entity type
|
|
432
464
|
if (primaryKey.length === 0) {
|
|
@@ -452,16 +484,70 @@ function inferConstraints(entityName, typeMap) {
|
|
|
452
484
|
unique: [], // Would need constraint info to populate
|
|
453
485
|
};
|
|
454
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Infer a single-row lookup key from an Update/Delete input object.
|
|
489
|
+
*
|
|
490
|
+
* Priority:
|
|
491
|
+
* 1. Canonical keys: id, nodeId, rowId
|
|
492
|
+
* 2. Single non-patch/non-clientMutationId scalar-ish field
|
|
493
|
+
*
|
|
494
|
+
* If multiple possible key fields remain, return null to avoid guessing.
|
|
495
|
+
*/
|
|
496
|
+
function inferPrimaryKeyFromInputObject(inputType) {
|
|
497
|
+
const inputFields = inputType?.inputFields ?? [];
|
|
498
|
+
if (inputFields.length === 0)
|
|
499
|
+
return null;
|
|
500
|
+
const canonicalKey = inputFields.find((field) => field.name === 'id' || field.name === 'nodeId' || field.name === 'rowId');
|
|
501
|
+
if (canonicalKey)
|
|
502
|
+
return canonicalKey;
|
|
503
|
+
const candidates = inputFields.filter((field) => {
|
|
504
|
+
if (field.name === 'clientMutationId')
|
|
505
|
+
return false;
|
|
506
|
+
const baseTypeName = getBaseTypeName(field.type);
|
|
507
|
+
const lowerName = field.name.toLowerCase();
|
|
508
|
+
// Exclude patch payload fields (patch / fooPatch)
|
|
509
|
+
if (lowerName === 'patch' || lowerName.endsWith('patch'))
|
|
510
|
+
return false;
|
|
511
|
+
if (baseTypeName?.endsWith('Patch'))
|
|
512
|
+
return false;
|
|
513
|
+
return true;
|
|
514
|
+
});
|
|
515
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Infer the patch field name from an Update input type.
|
|
519
|
+
*
|
|
520
|
+
* PostGraphile v5 uses entity-specific patch field names:
|
|
521
|
+
* UpdateUserInput → { id, userPatch: UserPatch }
|
|
522
|
+
* UpdateDatabaseInput → { id, databasePatch: DatabasePatch }
|
|
523
|
+
*
|
|
524
|
+
* Pattern: {lcFirst(entityTypeName)}Patch
|
|
525
|
+
*/
|
|
526
|
+
function inferPatchFieldName(entityName, typeMap) {
|
|
527
|
+
const updateInputName = `Update${entityName}Input`;
|
|
528
|
+
const updateInput = typeMap.get(updateInputName);
|
|
529
|
+
const inputFields = updateInput?.inputFields ?? [];
|
|
530
|
+
// Find the field whose type name ends in 'Patch'
|
|
531
|
+
const patchField = inputFields.find((f) => {
|
|
532
|
+
const baseName = getBaseTypeName(f.type);
|
|
533
|
+
return baseName?.endsWith('Patch');
|
|
534
|
+
});
|
|
535
|
+
if (patchField)
|
|
536
|
+
return patchField.name;
|
|
537
|
+
// Fallback: compute from entity name (v5 default inflection)
|
|
538
|
+
return lcFirst(entityName) + 'Patch';
|
|
539
|
+
}
|
|
455
540
|
// ============================================================================
|
|
456
541
|
// Inflection Building
|
|
457
542
|
// ============================================================================
|
|
458
543
|
/**
|
|
459
544
|
* Build inflection map from discovered types
|
|
460
545
|
*/
|
|
461
|
-
function buildInflection(entityName, typeMap) {
|
|
546
|
+
function buildInflection(entityName, typeMap, entityToConnection) {
|
|
462
547
|
const pluralName = pluralize(entityName);
|
|
463
548
|
const singularFieldName = lcFirst(entityName);
|
|
464
549
|
const pluralFieldName = lcFirst(pluralName);
|
|
550
|
+
const connectionTypeName = entityToConnection.get(entityName) ?? `${pluralName}Connection`;
|
|
465
551
|
// Check which types actually exist in the schema
|
|
466
552
|
const hasFilter = typeMap.has(`${entityName}Filter`);
|
|
467
553
|
const hasPatch = typeMap.has(`${entityName}Patch`);
|
|
@@ -475,7 +561,7 @@ function buildInflection(entityName, typeMap) {
|
|
|
475
561
|
allRows: pluralFieldName,
|
|
476
562
|
allRowsSimple: pluralFieldName,
|
|
477
563
|
conditionType: `${entityName}Condition`,
|
|
478
|
-
connection:
|
|
564
|
+
connection: connectionTypeName,
|
|
479
565
|
createField: `create${entityName}`,
|
|
480
566
|
createInputType: `Create${entityName}Input`,
|
|
481
567
|
createPayloadType: `Create${entityName}Payload`,
|
|
@@ -228,7 +228,9 @@ export function getTableOperationNames(tables) {
|
|
|
228
228
|
if (table.query) {
|
|
229
229
|
// Add exact query names from _meta
|
|
230
230
|
queries.add(table.query.all);
|
|
231
|
-
|
|
231
|
+
if (table.query.one) {
|
|
232
|
+
queries.add(table.query.one);
|
|
233
|
+
}
|
|
232
234
|
// Add exact mutation names from _meta
|
|
233
235
|
mutations.add(table.query.create);
|
|
234
236
|
if (table.query.update)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Query and mutation generator exports
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
|
|
5
|
+
* These are retained for backward compatibility with existing v4 consumers.
|
|
3
6
|
*/
|
|
4
7
|
export { convertToSelectionOptions, getAvailableRelations, isRelationalField, validateFieldSelection, } from './field-selector';
|
|
5
8
|
export { buildCount, buildFindOne, buildSelect, cleanTableToMetaObject, createASTQueryBuilder, generateIntrospectionSchema, toCamelCasePlural, toOrderByTypeName, } from './select';
|
package/esm/generators/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Query and mutation generator exports
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
|
|
5
|
+
* These are retained for backward compatibility with existing v4 consumers.
|
|
3
6
|
*/
|
|
4
7
|
// Field selector utilities
|
|
5
8
|
export { convertToSelectionOptions, getAvailableRelations, isRelationalField, validateFieldSelection, } from './field-selector';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Uses AST-based approach for PostGraphile-compatible mutations
|
|
4
4
|
*/
|
|
5
5
|
import * as t from 'gql-ast';
|
|
6
|
-
import { print } from 'graphql';
|
|
6
|
+
import { OperationTypeNode, print } from 'graphql';
|
|
7
7
|
import { camelize } from 'inflekt';
|
|
8
8
|
import { TypedDocumentString } from '../client/typed-document';
|
|
9
9
|
import { getCustomAstForCleanField, requiresSubfieldSelection, } from '../core/custom-ast';
|
|
@@ -55,7 +55,7 @@ export function buildPostGraphileCreate(table, _allTables, _options = {}) {
|
|
|
55
55
|
const ast = t.document({
|
|
56
56
|
definitions: [
|
|
57
57
|
t.operationDefinition({
|
|
58
|
-
operation:
|
|
58
|
+
operation: OperationTypeNode.MUTATION,
|
|
59
59
|
name: `${mutationName}Mutation`,
|
|
60
60
|
variableDefinitions,
|
|
61
61
|
selectionSet: t.selectionSet({
|
|
@@ -114,7 +114,7 @@ export function buildPostGraphileUpdate(table, _allTables, _options = {}) {
|
|
|
114
114
|
const ast = t.document({
|
|
115
115
|
definitions: [
|
|
116
116
|
t.operationDefinition({
|
|
117
|
-
operation:
|
|
117
|
+
operation: OperationTypeNode.MUTATION,
|
|
118
118
|
name: `${mutationName}Mutation`,
|
|
119
119
|
variableDefinitions,
|
|
120
120
|
selectionSet: t.selectionSet({
|
|
@@ -172,7 +172,7 @@ export function buildPostGraphileDelete(table, _allTables, _options = {}) {
|
|
|
172
172
|
const ast = t.document({
|
|
173
173
|
definitions: [
|
|
174
174
|
t.operationDefinition({
|
|
175
|
-
operation:
|
|
175
|
+
operation: OperationTypeNode.MUTATION,
|
|
176
176
|
name: `${mutationName}Mutation`,
|
|
177
177
|
variableDefinitions,
|
|
178
178
|
selectionSet: t.selectionSet({
|
package/esm/generators/select.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Uses AST-based approach for all query generation
|
|
4
4
|
*/
|
|
5
5
|
import * as t from 'gql-ast';
|
|
6
|
-
import { print } from 'graphql';
|
|
6
|
+
import { OperationTypeNode, print } from 'graphql';
|
|
7
7
|
import { camelize, pluralize } from 'inflekt';
|
|
8
8
|
import { TypedDocumentString } from '../client/typed-document';
|
|
9
9
|
import { getCustomAstForCleanField, requiresSubfieldSelection, } from '../core/custom-ast';
|
|
@@ -368,7 +368,7 @@ function generateSelectQueryAST(table, allTables, selection, options) {
|
|
|
368
368
|
const ast = t.document({
|
|
369
369
|
definitions: [
|
|
370
370
|
t.operationDefinition({
|
|
371
|
-
operation:
|
|
371
|
+
operation: OperationTypeNode.QUERY,
|
|
372
372
|
name: `${pluralName}Query`,
|
|
373
373
|
variableDefinitions,
|
|
374
374
|
selectionSet: t.selectionSet({
|
|
@@ -566,7 +566,7 @@ function generateFindOneQueryAST(table) {
|
|
|
566
566
|
const ast = t.document({
|
|
567
567
|
definitions: [
|
|
568
568
|
t.operationDefinition({
|
|
569
|
-
operation:
|
|
569
|
+
operation: OperationTypeNode.QUERY,
|
|
570
570
|
name: `${singularName}Query`,
|
|
571
571
|
variableDefinitions: [
|
|
572
572
|
t.variableDefinition({
|
|
@@ -605,7 +605,7 @@ function generateCountQueryAST(table) {
|
|
|
605
605
|
const ast = t.document({
|
|
606
606
|
definitions: [
|
|
607
607
|
t.operationDefinition({
|
|
608
|
-
operation:
|
|
608
|
+
operation: OperationTypeNode.QUERY,
|
|
609
609
|
name: `${pluralName}CountQuery`,
|
|
610
610
|
variableDefinitions: [
|
|
611
611
|
t.variableDefinition({
|
package/esm/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @constructive-io/graphql-codegen
|
|
3
3
|
*
|
|
4
4
|
* GraphQL SDK generator for Constructive databases.
|
|
5
|
-
* Introspects via
|
|
5
|
+
* Introspects via standard GraphQL introspection and generates typed queries, mutations,
|
|
6
6
|
* and React Query v5 hooks.
|
|
7
7
|
*/
|
|
8
8
|
export * from './types';
|
package/esm/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @constructive-io/graphql-codegen
|
|
3
3
|
*
|
|
4
4
|
* GraphQL SDK generator for Constructive databases.
|
|
5
|
-
* Introspects via
|
|
5
|
+
* Introspects via standard GraphQL introspection and generates typed queries, mutations,
|
|
6
6
|
* and React Query v5 hooks.
|
|
7
7
|
*/
|
|
8
8
|
// Core types
|
package/esm/types/schema.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface CleanTable {
|
|
|
11
11
|
relations: CleanRelations;
|
|
12
12
|
/** PostGraphile inflection rules for this table */
|
|
13
13
|
inflection?: TableInflection;
|
|
14
|
-
/** Query operation names from
|
|
14
|
+
/** Query operation names from introspection */
|
|
15
15
|
query?: TableQueryNames;
|
|
16
16
|
/** Constraint information */
|
|
17
17
|
constraints?: TableConstraints;
|
|
@@ -66,19 +66,21 @@ export interface TableInflection {
|
|
|
66
66
|
updatePayloadType: string | null;
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Query operation names from
|
|
69
|
+
* Query operation names from introspection
|
|
70
70
|
*/
|
|
71
71
|
export interface TableQueryNames {
|
|
72
72
|
/** All rows query name */
|
|
73
73
|
all: string;
|
|
74
74
|
/** Single row query name */
|
|
75
|
-
one: string;
|
|
75
|
+
one: string | null;
|
|
76
76
|
/** Create mutation name */
|
|
77
77
|
create: string;
|
|
78
78
|
/** Update mutation name */
|
|
79
79
|
update: string | null;
|
|
80
80
|
/** Delete mutation name */
|
|
81
81
|
delete: string | null;
|
|
82
|
+
/** Patch field name in update mutation input (e.g., "userPatch" for UpdateUserInput) */
|
|
83
|
+
patchFieldName?: string;
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
86
|
* Table constraints
|
package/generators/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Query and mutation generator exports
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
|
|
5
|
+
* These are retained for backward compatibility with existing v4 consumers.
|
|
3
6
|
*/
|
|
4
7
|
export { convertToSelectionOptions, getAvailableRelations, isRelationalField, validateFieldSelection, } from './field-selector';
|
|
5
8
|
export { buildCount, buildFindOne, buildSelect, cleanTableToMetaObject, createASTQueryBuilder, generateIntrospectionSchema, toCamelCasePlural, toOrderByTypeName, } from './select';
|
package/generators/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Query and mutation generator exports
|
|
4
|
+
*
|
|
5
|
+
* @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
|
|
6
|
+
* These are retained for backward compatibility with existing v4 consumers.
|
|
4
7
|
*/
|
|
5
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
9
|
exports.buildPostGraphileUpdate = exports.buildPostGraphileDelete = exports.buildPostGraphileCreate = exports.toOrderByTypeName = exports.toCamelCasePlural = exports.generateIntrospectionSchema = exports.createASTQueryBuilder = exports.cleanTableToMetaObject = exports.buildSelect = exports.buildFindOne = exports.buildCount = exports.validateFieldSelection = exports.isRelationalField = exports.getAvailableRelations = exports.convertToSelectionOptions = void 0;
|
package/generators/mutations.js
CHANGED
|
@@ -93,7 +93,7 @@ function buildPostGraphileCreate(table, _allTables, _options = {}) {
|
|
|
93
93
|
const ast = t.document({
|
|
94
94
|
definitions: [
|
|
95
95
|
t.operationDefinition({
|
|
96
|
-
operation:
|
|
96
|
+
operation: graphql_1.OperationTypeNode.MUTATION,
|
|
97
97
|
name: `${mutationName}Mutation`,
|
|
98
98
|
variableDefinitions,
|
|
99
99
|
selectionSet: t.selectionSet({
|
|
@@ -152,7 +152,7 @@ function buildPostGraphileUpdate(table, _allTables, _options = {}) {
|
|
|
152
152
|
const ast = t.document({
|
|
153
153
|
definitions: [
|
|
154
154
|
t.operationDefinition({
|
|
155
|
-
operation:
|
|
155
|
+
operation: graphql_1.OperationTypeNode.MUTATION,
|
|
156
156
|
name: `${mutationName}Mutation`,
|
|
157
157
|
variableDefinitions,
|
|
158
158
|
selectionSet: t.selectionSet({
|
|
@@ -210,7 +210,7 @@ function buildPostGraphileDelete(table, _allTables, _options = {}) {
|
|
|
210
210
|
const ast = t.document({
|
|
211
211
|
definitions: [
|
|
212
212
|
t.operationDefinition({
|
|
213
|
-
operation:
|
|
213
|
+
operation: graphql_1.OperationTypeNode.MUTATION,
|
|
214
214
|
name: `${mutationName}Mutation`,
|
|
215
215
|
variableDefinitions,
|
|
216
216
|
selectionSet: t.selectionSet({
|
package/generators/select.js
CHANGED
|
@@ -411,7 +411,7 @@ function generateSelectQueryAST(table, allTables, selection, options) {
|
|
|
411
411
|
const ast = t.document({
|
|
412
412
|
definitions: [
|
|
413
413
|
t.operationDefinition({
|
|
414
|
-
operation:
|
|
414
|
+
operation: graphql_1.OperationTypeNode.QUERY,
|
|
415
415
|
name: `${pluralName}Query`,
|
|
416
416
|
variableDefinitions,
|
|
417
417
|
selectionSet: t.selectionSet({
|
|
@@ -609,7 +609,7 @@ function generateFindOneQueryAST(table) {
|
|
|
609
609
|
const ast = t.document({
|
|
610
610
|
definitions: [
|
|
611
611
|
t.operationDefinition({
|
|
612
|
-
operation:
|
|
612
|
+
operation: graphql_1.OperationTypeNode.QUERY,
|
|
613
613
|
name: `${singularName}Query`,
|
|
614
614
|
variableDefinitions: [
|
|
615
615
|
t.variableDefinition({
|
|
@@ -648,7 +648,7 @@ function generateCountQueryAST(table) {
|
|
|
648
648
|
const ast = t.document({
|
|
649
649
|
definitions: [
|
|
650
650
|
t.operationDefinition({
|
|
651
|
-
operation:
|
|
651
|
+
operation: graphql_1.OperationTypeNode.QUERY,
|
|
652
652
|
name: `${pluralName}CountQuery`,
|
|
653
653
|
variableDefinitions: [
|
|
654
654
|
t.variableDefinition({
|