@constructive-io/graphql-codegen 3.3.2 → 4.0.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.
Files changed (71) hide show
  1. package/README.md +0 -4
  2. package/cli/index.js +5 -4
  3. package/cli/shared.js +9 -2
  4. package/core/ast.js +6 -5
  5. package/core/codegen/custom-mutations.js +22 -22
  6. package/core/codegen/custom-queries.js +24 -17
  7. package/core/codegen/hooks-ast.d.ts +1 -1
  8. package/core/codegen/hooks-ast.js +22 -5
  9. package/core/codegen/index.d.ts +1 -1
  10. package/core/codegen/mutations.js +16 -12
  11. package/core/codegen/orm/custom-ops-generator.js +37 -3
  12. package/core/codegen/orm/input-types-generator.js +161 -89
  13. package/core/codegen/orm/model-generator.js +72 -73
  14. package/core/codegen/orm/select-types.d.ts +27 -17
  15. package/core/codegen/queries.js +37 -25
  16. package/core/codegen/schema-types-generator.js +21 -0
  17. package/core/codegen/templates/hooks-selection.ts +12 -0
  18. package/core/codegen/templates/query-builder.ts +103 -59
  19. package/core/codegen/templates/select-types.ts +59 -33
  20. package/core/codegen/types.js +26 -0
  21. package/core/codegen/utils.d.ts +1 -1
  22. package/core/codegen/utils.js +1 -1
  23. package/core/custom-ast.js +9 -8
  24. package/core/database/index.js +2 -3
  25. package/core/index.d.ts +2 -0
  26. package/core/index.js +2 -0
  27. package/core/introspect/infer-tables.js +144 -58
  28. package/core/introspect/transform-schema.d.ts +1 -1
  29. package/core/introspect/transform-schema.js +3 -1
  30. package/esm/cli/index.js +5 -4
  31. package/esm/cli/shared.js +9 -2
  32. package/esm/core/ast.js +6 -5
  33. package/esm/core/codegen/custom-mutations.js +23 -23
  34. package/esm/core/codegen/custom-queries.js +25 -18
  35. package/esm/core/codegen/hooks-ast.d.ts +1 -1
  36. package/esm/core/codegen/hooks-ast.js +22 -5
  37. package/esm/core/codegen/index.d.ts +1 -1
  38. package/esm/core/codegen/mutations.js +16 -12
  39. package/esm/core/codegen/orm/custom-ops-generator.js +38 -4
  40. package/esm/core/codegen/orm/input-types-generator.js +163 -91
  41. package/esm/core/codegen/orm/model-generator.js +73 -74
  42. package/esm/core/codegen/orm/select-types.d.ts +27 -17
  43. package/esm/core/codegen/queries.js +37 -25
  44. package/esm/core/codegen/schema-types-generator.js +21 -0
  45. package/esm/core/codegen/types.js +26 -0
  46. package/esm/core/codegen/utils.d.ts +1 -1
  47. package/esm/core/codegen/utils.js +1 -1
  48. package/esm/core/custom-ast.js +9 -8
  49. package/esm/core/database/index.js +2 -3
  50. package/esm/core/index.d.ts +2 -0
  51. package/esm/core/index.js +2 -0
  52. package/esm/core/introspect/infer-tables.js +144 -58
  53. package/esm/core/introspect/transform-schema.d.ts +1 -1
  54. package/esm/core/introspect/transform-schema.js +3 -1
  55. package/esm/generators/field-selector.js +1 -0
  56. package/esm/generators/index.d.ts +3 -0
  57. package/esm/generators/index.js +3 -0
  58. package/esm/generators/mutations.js +4 -4
  59. package/esm/generators/select.js +4 -4
  60. package/esm/index.d.ts +1 -1
  61. package/esm/index.js +1 -1
  62. package/esm/types/schema.d.ts +5 -3
  63. package/generators/field-selector.js +1 -0
  64. package/generators/index.d.ts +3 -0
  65. package/generators/index.js +3 -0
  66. package/generators/mutations.js +3 -3
  67. package/generators/select.js +3 -3
  68. package/index.d.ts +1 -1
  69. package/index.js +1 -1
  70. package/package.json +10 -10
  71. 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
  }
@@ -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: Detect entity types by finding Connection types
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
- * Detect entity types by finding Connection types in the schema
107
+ * Infer entity <-> connection mappings from schema types.
112
108
  *
113
- * PostGraphile generates a {PluralName}Connection type for each table.
114
- * From this, we can derive the entity type name.
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 detectEntityTypes(types) {
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 pluralName = connectionMatch[1]; // e.g., "Users" from "UsersConnection"
129
- const singularName = singularize(pluralName); // e.g., "User"
130
- // Verify the entity type actually exists
131
- if (typeNames.has(singularName)) {
132
- entityNames.add(singularName);
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 entityNames;
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, typeMap);
176
+ const relations = inferRelations(entityType, entityToConnection, connectionToEntity);
146
177
  // Match query and mutation operations
147
- const queryOps = matchQueryOperations(entityName, queryFields, typeMap);
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 ?? lcFirst(entityName),
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, typeMap)) {
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, typeMap) {
216
- const connectionName = `${pluralize(typeName)}Connection`;
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, typeMap) {
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 relation = inferHasManyOrManyToMany(field, baseTypeName, typeMap);
252
- if (relation.type === 'manyToMany') {
253
- manyToMany.push(relation.relation);
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(relation.relation);
289
+ hasMany.push(resolvedRelation.relation);
257
290
  }
258
291
  continue;
259
292
  }
260
293
  // Check for entity type → belongsTo
261
- if (isEntityType(baseTypeName, typeMap)) {
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, typeMap) {
280
- // Extract the related entity name from Connection type
281
- const match = connectionTypeName.match(PATTERNS.connection);
282
- const relatedPluralName = match ? match[1] : connectionTypeName;
283
- const relatedEntityName = singularize(relatedPluralName);
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, typeMap) {
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
- // Check update input for id field
416
- const inputToCheck = updateInput || deleteInput;
417
- if (inputToCheck?.inputFields) {
418
- const idField = inputToCheck.inputFields.find((f) => f.name === 'id' || f.name === 'nodeId');
419
- if (idField) {
420
- primaryKey.push({
421
- name: 'primary',
422
- fields: [
423
- {
424
- name: idField.name,
425
- type: convertToCleanFieldType(idField.type),
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: `${pluralName}Connection`,
564
+ connection: connectionTypeName,
479
565
  createField: `create${entityName}`,
480
566
  createInputType: `Create${entityName}Input`,
481
567
  createPayloadType: `Create${entityName}Payload`,
@@ -58,7 +58,7 @@ export declare function getTableOperationNames(tables: Array<{
58
58
  name: string;
59
59
  query?: {
60
60
  all: string;
61
- one: string;
61
+ one: string | null;
62
62
  create: string;
63
63
  update: string | null;
64
64
  delete: string | null;
@@ -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
- queries.add(table.query.one);
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)
@@ -241,6 +241,7 @@ function getRelatedTableScalarFields(relationField, table, allTables) {
241
241
  };
242
242
  // Always try to include stable identifiers first.
243
243
  push('id');
244
+ push('rowId');
244
245
  push('nodeId');
245
246
  for (const fieldName of preferred)
246
247
  push(fieldName);
@@ -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';
@@ -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: 'mutation',
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: 'mutation',
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: 'mutation',
175
+ operation: OperationTypeNode.MUTATION,
176
176
  name: `${mutationName}Mutation`,
177
177
  variableDefinitions,
178
178
  selectionSet: t.selectionSet({
@@ -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: 'query',
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: 'query',
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: 'query',
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 _meta query and generates typed queries, mutations,
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 _meta query and generates typed queries, mutations,
5
+ * Introspects via standard GraphQL introspection and generates typed queries, mutations,
6
6
  * and React Query v5 hooks.
7
7
  */
8
8
  // Core types
@@ -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 _meta */
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 _meta.query
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
@@ -247,6 +247,7 @@ function getRelatedTableScalarFields(relationField, table, allTables) {
247
247
  };
248
248
  // Always try to include stable identifiers first.
249
249
  push('id');
250
+ push('rowId');
250
251
  push('nodeId');
251
252
  for (const fieldName of preferred)
252
253
  push(fieldName);
@@ -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';
@@ -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;
@@ -93,7 +93,7 @@ function buildPostGraphileCreate(table, _allTables, _options = {}) {
93
93
  const ast = t.document({
94
94
  definitions: [
95
95
  t.operationDefinition({
96
- operation: 'mutation',
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: 'mutation',
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: 'mutation',
213
+ operation: graphql_1.OperationTypeNode.MUTATION,
214
214
  name: `${mutationName}Mutation`,
215
215
  variableDefinitions,
216
216
  selectionSet: t.selectionSet({
@@ -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: 'query',
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: 'query',
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: 'query',
651
+ operation: graphql_1.OperationTypeNode.QUERY,
652
652
  name: `${pluralName}CountQuery`,
653
653
  variableDefinitions: [
654
654
  t.variableDefinition({