@constructive-io/graphql-codegen 4.6.0 → 4.7.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.
Files changed (75) hide show
  1. package/client/error.d.ts +2 -93
  2. package/client/error.js +9 -273
  3. package/client/execute.d.ts +2 -55
  4. package/client/execute.js +5 -120
  5. package/client/typed-document.d.ts +2 -29
  6. package/client/typed-document.js +3 -39
  7. package/core/ast.d.ts +8 -10
  8. package/core/ast.js +17 -592
  9. package/core/custom-ast.d.ts +5 -33
  10. package/core/custom-ast.js +16 -203
  11. package/core/introspect/infer-tables.d.ts +2 -40
  12. package/core/introspect/infer-tables.js +4 -653
  13. package/core/introspect/schema-query.d.ts +3 -18
  14. package/core/introspect/schema-query.js +3 -118
  15. package/core/introspect/transform-schema.d.ts +2 -84
  16. package/core/introspect/transform-schema.js +14 -279
  17. package/core/introspect/transform.d.ts +2 -18
  18. package/core/introspect/transform.js +6 -39
  19. package/core/meta-object/convert.d.ts +2 -63
  20. package/core/meta-object/convert.js +4 -59
  21. package/core/meta-object/validate.d.ts +2 -7
  22. package/core/meta-object/validate.js +4 -30
  23. package/core/query-builder.d.ts +7 -46
  24. package/core/query-builder.js +8 -408
  25. package/core/types.d.ts +9 -139
  26. package/core/types.js +12 -26
  27. package/esm/client/error.d.ts +2 -93
  28. package/esm/client/error.js +2 -269
  29. package/esm/client/execute.d.ts +2 -55
  30. package/esm/client/execute.js +2 -118
  31. package/esm/client/typed-document.d.ts +2 -29
  32. package/esm/client/typed-document.js +2 -38
  33. package/esm/core/ast.d.ts +8 -10
  34. package/esm/core/ast.js +8 -550
  35. package/esm/core/custom-ast.d.ts +5 -33
  36. package/esm/core/custom-ast.js +5 -160
  37. package/esm/core/introspect/infer-tables.d.ts +2 -40
  38. package/esm/core/introspect/infer-tables.js +2 -652
  39. package/esm/core/introspect/schema-query.d.ts +3 -18
  40. package/esm/core/introspect/schema-query.js +2 -118
  41. package/esm/core/introspect/transform-schema.d.ts +2 -84
  42. package/esm/core/introspect/transform-schema.js +2 -269
  43. package/esm/core/introspect/transform.d.ts +2 -18
  44. package/esm/core/introspect/transform.js +2 -36
  45. package/esm/core/meta-object/convert.d.ts +2 -63
  46. package/esm/core/meta-object/convert.js +2 -58
  47. package/esm/core/meta-object/validate.d.ts +2 -7
  48. package/esm/core/meta-object/validate.js +2 -26
  49. package/esm/core/query-builder.d.ts +7 -46
  50. package/esm/core/query-builder.js +5 -373
  51. package/esm/core/types.d.ts +9 -139
  52. package/esm/core/types.js +9 -24
  53. package/esm/generators/field-selector.d.ts +5 -28
  54. package/esm/generators/field-selector.js +5 -354
  55. package/esm/generators/mutations.d.ts +5 -29
  56. package/esm/generators/mutations.js +5 -195
  57. package/esm/generators/naming-helpers.d.ts +6 -0
  58. package/esm/generators/naming-helpers.js +6 -0
  59. package/esm/generators/select.d.ts +6 -48
  60. package/esm/generators/select.js +6 -634
  61. package/esm/types/query.d.ts +9 -0
  62. package/esm/types/schema.d.ts +4 -0
  63. package/generators/field-selector.d.ts +5 -28
  64. package/generators/field-selector.js +12 -360
  65. package/generators/mutations.d.ts +5 -29
  66. package/generators/mutations.js +9 -231
  67. package/generators/naming-helpers.d.ts +6 -0
  68. package/generators/naming-helpers.js +20 -0
  69. package/generators/select.d.ts +6 -48
  70. package/generators/select.js +17 -677
  71. package/package.json +7 -6
  72. package/types/query.d.ts +9 -0
  73. package/types/schema.d.ts +4 -0
  74. package/core/meta-object/format.json +0 -93
  75. package/esm/core/meta-object/format.json +0 -93
@@ -1,657 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inferTablesFromIntrospection = inferTablesFromIntrospection;
3
+ exports.inferTablesFromIntrospection = void 0;
4
4
  /**
5
- * Infer PostGraphile table metadata from standard GraphQL introspection
6
- *
7
- * This module replaces the need for the _meta query by recognizing PostGraphile's
8
- * naming conventions and type patterns from standard GraphQL introspection.
9
- *
10
- * Key patterns recognized:
11
- * - Connection types: {PluralName}Connection → entity name
12
- * - Filter types: {Name}Filter
13
- * - Input types: Create{Name}Input, Update{Name}Input, Delete{Name}Input
14
- * - Payload types: Create{Name}Payload, Update{Name}Payload, Delete{Name}Payload
15
- * - Query operations: {pluralName} (list), {singularName} (single)
16
- * - Mutation operations: create{Name}, update{Name}, delete{Name}
5
+ * Re-export table inference from @constructive-io/graphql-query.
17
6
  */
18
- const inflekt_1 = require("inflekt");
19
- const utils_1 = require("../codegen/utils");
20
- const introspection_1 = require("../../types/introspection");
21
- // ============================================================================
22
- // Pattern Matching Constants
23
- // ============================================================================
24
- /**
25
- * PostGraphile naming patterns for type detection
26
- */
27
- const PATTERNS = {
28
- // Type suffixes
29
- connection: /^(.+)Connection$/,
30
- edge: /^(.+)Edge$/,
31
- filter: /^(.+)Filter$/,
32
- condition: /^(.+)Condition$/,
33
- orderBy: /^(.+)OrderBy$/,
34
- patch: /^(.+)Patch$/,
35
- // Input type patterns
36
- createInput: /^Create(.+)Input$/,
37
- updateInput: /^Update(.+)Input$/,
38
- deleteInput: /^Delete(.+)Input$/,
39
- // Payload type patterns
40
- createPayload: /^Create(.+)Payload$/,
41
- updatePayload: /^Update(.+)Payload$/,
42
- deletePayload: /^Delete(.+)Payload$/,
43
- // Mutation name patterns (camelCase)
44
- createMutation: /^create([A-Z][a-zA-Z0-9]*)$/,
45
- updateMutation: /^update([A-Z][a-zA-Z0-9]*)$/,
46
- deleteMutation: /^delete([A-Z][a-zA-Z0-9]*)$/,
47
- };
48
- /**
49
- * Built-in GraphQL types to ignore
50
- */
51
- const BUILTIN_TYPES = new Set([
52
- 'Query',
53
- 'Mutation',
54
- 'Subscription',
55
- 'String',
56
- 'Int',
57
- 'Float',
58
- 'Boolean',
59
- 'ID',
60
- // PostGraphile built-in types
61
- 'Node',
62
- 'PageInfo',
63
- 'Cursor',
64
- 'UUID',
65
- 'Datetime',
66
- 'Date',
67
- 'Time',
68
- 'JSON',
69
- 'BigInt',
70
- 'BigFloat',
71
- ]);
72
- /**
73
- * Types that start with __ are internal GraphQL types
74
- */
75
- function isInternalType(name) {
76
- return name.startsWith('__');
77
- }
78
- /**
79
- * Infer CleanTable[] from GraphQL introspection by recognizing PostGraphile patterns
80
- *
81
- * @param introspection - Standard GraphQL introspection response
82
- * @param options - Optional configuration
83
- * @returns Array of CleanTable objects compatible with existing generators
84
- */
85
- function inferTablesFromIntrospection(introspection, options = {}) {
86
- const { __schema: schema } = introspection;
87
- const { types, queryType, mutationType } = schema;
88
- const commentsEnabled = options.comments !== false;
89
- // Build lookup maps for efficient access
90
- const typeMap = buildTypeMap(types);
91
- const { entityNames, entityToConnection, connectionToEntity } = buildEntityConnectionMaps(types, typeMap);
92
- const queryFields = getTypeFields(typeMap.get(queryType.name));
93
- const mutationFields = mutationType
94
- ? getTypeFields(typeMap.get(mutationType.name))
95
- : [];
96
- // Step 1: Build CleanTable for each inferred entity
97
- const tables = [];
98
- for (const entityName of entityNames) {
99
- const entityType = typeMap.get(entityName);
100
- if (!entityType)
101
- continue;
102
- // Infer all metadata for this entity
103
- const { table, hasRealOperation } = buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields, entityToConnection, connectionToEntity, commentsEnabled);
104
- // Only include tables that have at least one real operation
105
- if (hasRealOperation) {
106
- tables.push(table);
107
- }
108
- }
109
- return tables;
110
- }
111
- /**
112
- * Infer entity <-> connection mappings from schema types.
113
- *
114
- * Prefer deriving entity names from the `nodes` field of connection types.
115
- * This is robust across v4/v5 naming variations (e.g. `UsersConnection` and
116
- * `UserConnection`).
117
- */
118
- function buildEntityConnectionMaps(types, typeMap) {
119
- const entityNames = new Set();
120
- const entityToConnection = new Map();
121
- const connectionToEntity = new Map();
122
- const typeNames = new Set(types.map((t) => t.name));
123
- for (const type of types) {
124
- // Skip internal types
125
- if (isInternalType(type.name))
126
- continue;
127
- if (BUILTIN_TYPES.has(type.name))
128
- continue;
129
- // Check for Connection pattern
130
- const connectionMatch = type.name.match(PATTERNS.connection);
131
- if (connectionMatch) {
132
- const fallbackEntityName = (0, inflekt_1.singularize)(connectionMatch[1]);
133
- const entityName = resolveEntityNameFromConnectionType(type, typeMap) ?? fallbackEntityName;
134
- // Verify the entity type actually exists and is not a built-in/internal type.
135
- if (typeNames.has(entityName) &&
136
- !BUILTIN_TYPES.has(entityName) &&
137
- !isInternalType(entityName)) {
138
- entityNames.add(entityName);
139
- entityToConnection.set(entityName, type.name);
140
- connectionToEntity.set(type.name, entityName);
141
- }
142
- }
143
- }
144
- return {
145
- entityNames,
146
- entityToConnection,
147
- connectionToEntity,
148
- };
149
- }
150
- /**
151
- * Attempt to resolve an entity name from a connection type by inspecting its
152
- * `nodes` field.
153
- */
154
- function resolveEntityNameFromConnectionType(connectionType, typeMap) {
155
- if (!connectionType.fields)
156
- return null;
157
- const nodesField = connectionType.fields.find((field) => field.name === 'nodes');
158
- if (!nodesField)
159
- return null;
160
- const nodeTypeName = (0, introspection_1.getBaseTypeName)(nodesField.type);
161
- if (!nodeTypeName)
162
- return null;
163
- const nodeType = typeMap.get(nodeTypeName);
164
- if (!nodeType || nodeType.kind !== 'OBJECT')
165
- return null;
166
- if (nodeTypeName.endsWith('Connection'))
167
- return null;
168
- if (BUILTIN_TYPES.has(nodeTypeName))
169
- return null;
170
- if (isInternalType(nodeTypeName))
171
- return null;
172
- return nodeTypeName;
173
- }
174
- /**
175
- * Build a complete CleanTable from an entity type
176
- */
177
- function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationFields, entityToConnection, connectionToEntity, commentsEnabled) {
178
- // Extract scalar fields from entity type
179
- const fields = extractEntityFields(entityType, typeMap, entityToConnection, commentsEnabled);
180
- // Infer relations from entity fields
181
- const relations = inferRelations(entityType, entityToConnection, connectionToEntity);
182
- // Match query and mutation operations
183
- const queryOps = matchQueryOperations(entityName, queryFields, entityToConnection);
184
- const mutationOps = matchMutationOperations(entityName, mutationFields);
185
- // Check if we found at least one real operation (not a fallback)
186
- const hasRealOperation = !!(queryOps.all ||
187
- queryOps.one ||
188
- mutationOps.create ||
189
- mutationOps.update ||
190
- mutationOps.delete);
191
- // Infer primary key from mutation inputs
192
- const constraints = inferConstraints(entityName, typeMap);
193
- // Infer the patch field name from UpdateXxxInput (e.g., "userPatch")
194
- const patchFieldName = inferPatchFieldName(entityName, typeMap);
195
- // Build inflection map from discovered types
196
- const inflection = buildInflection(entityName, typeMap, entityToConnection);
197
- // Combine query operations with fallbacks for UI purposes
198
- // (but hasRealOperation indicates if we should include this table)
199
- const query = {
200
- all: queryOps.all ?? (0, inflekt_1.lcFirst)((0, inflekt_1.pluralize)(entityName)),
201
- one: queryOps.one,
202
- create: mutationOps.create ?? `create${entityName}`,
203
- update: mutationOps.update,
204
- delete: mutationOps.delete,
205
- patchFieldName,
206
- };
207
- // Extract description from entity type (PostgreSQL COMMENT), strip smart comments
208
- const description = commentsEnabled ? (0, utils_1.stripSmartComments)(entityType.description) : undefined;
209
- return {
210
- table: {
211
- name: entityName,
212
- ...(description ? { description } : {}),
213
- fields,
214
- relations,
215
- inflection,
216
- query,
217
- constraints,
218
- },
219
- hasRealOperation,
220
- };
221
- }
222
- // ============================================================================
223
- // Field Extraction
224
- // ============================================================================
225
- /**
226
- * Extract scalar fields from an entity type
227
- * Excludes relation fields (those returning other entity types or connections)
228
- */
229
- function extractEntityFields(entityType, typeMap, entityToConnection, commentsEnabled) {
230
- const fields = [];
231
- if (!entityType.fields)
232
- return fields;
233
- for (const field of entityType.fields) {
234
- const baseTypeName = (0, introspection_1.getBaseTypeName)(field.type);
235
- if (!baseTypeName)
236
- continue;
237
- // Skip relation fields (those returning other objects or connections)
238
- const fieldType = typeMap.get(baseTypeName);
239
- if (fieldType?.kind === 'OBJECT') {
240
- // Check if it's a Connection type (hasMany) or entity type (belongsTo)
241
- if (baseTypeName.endsWith('Connection') ||
242
- isEntityType(baseTypeName, entityToConnection)) {
243
- continue; // Skip relation fields
244
- }
245
- }
246
- // Include scalar, enum, and other non-relation fields
247
- const fieldDescription = commentsEnabled ? (0, utils_1.stripSmartComments)(field.description) : undefined;
248
- fields.push({
249
- name: field.name,
250
- ...(fieldDescription ? { description: fieldDescription } : {}),
251
- type: convertToCleanFieldType(field.type),
252
- });
253
- }
254
- return fields;
255
- }
256
- /**
257
- * Check if a type name is an entity type (has a corresponding Connection)
258
- */
259
- function isEntityType(typeName, entityToConnection) {
260
- return entityToConnection.has(typeName);
261
- }
262
- /**
263
- * Convert IntrospectionTypeRef to CleanFieldType
264
- */
265
- function convertToCleanFieldType(typeRef) {
266
- const baseType = (0, introspection_1.unwrapType)(typeRef);
267
- const isArray = (0, introspection_1.isList)(typeRef);
268
- return {
269
- gqlType: baseType.name ?? 'Unknown',
270
- isArray,
271
- // PostgreSQL-specific fields are not available from introspection
272
- // They were optional anyway and not used by generators
273
- };
274
- }
275
- // ============================================================================
276
- // Relation Inference
277
- // ============================================================================
278
- /**
279
- * Infer relations from entity type fields
280
- */
281
- function inferRelations(entityType, entityToConnection, connectionToEntity) {
282
- const belongsTo = [];
283
- const hasMany = [];
284
- const manyToMany = [];
285
- if (!entityType.fields) {
286
- return { belongsTo, hasOne: [], hasMany, manyToMany };
287
- }
288
- for (const field of entityType.fields) {
289
- const baseTypeName = (0, introspection_1.getBaseTypeName)(field.type);
290
- if (!baseTypeName)
291
- continue;
292
- // Check for Connection type → hasMany or manyToMany
293
- if (baseTypeName.endsWith('Connection')) {
294
- const resolvedRelation = inferHasManyOrManyToMany(field, baseTypeName, connectionToEntity);
295
- if (resolvedRelation.type === 'manyToMany') {
296
- manyToMany.push(resolvedRelation.relation);
297
- }
298
- else {
299
- hasMany.push(resolvedRelation.relation);
300
- }
301
- continue;
302
- }
303
- // Check for entity type → belongsTo
304
- if (isEntityType(baseTypeName, entityToConnection)) {
305
- belongsTo.push({
306
- fieldName: field.name,
307
- isUnique: false, // Can't determine from introspection alone
308
- referencesTable: baseTypeName,
309
- type: baseTypeName,
310
- keys: [], // Would need FK info to populate
311
- });
312
- }
313
- }
314
- return { belongsTo, hasOne: [], hasMany, manyToMany };
315
- }
316
- /**
317
- * Determine if a Connection field is hasMany or manyToMany
318
- *
319
- * ManyToMany pattern: field name contains "By" and "And"
320
- * e.g., "productsByOrderItemOrderIdAndProductId"
321
- */
322
- function inferHasManyOrManyToMany(field, connectionTypeName, connectionToEntity) {
323
- // Resolve the related entity from discovered connection mappings first.
324
- const relatedEntityName = connectionToEntity.get(connectionTypeName) ?? (() => {
325
- const match = connectionTypeName.match(PATTERNS.connection);
326
- const relatedPluralName = match ? match[1] : connectionTypeName;
327
- return (0, inflekt_1.singularize)(relatedPluralName);
328
- })();
329
- // Check for manyToMany pattern in field name
330
- const isManyToMany = field.name.includes('By') && field.name.includes('And');
331
- if (isManyToMany) {
332
- // For ManyToMany, extract the actual entity name from the field name prefix
333
- // Field name pattern: {relatedEntities}By{JunctionTable}{Keys}
334
- // e.g., "usersByMembershipActorIdAndEntityId" → "users" → "User"
335
- // e.g., "productsByOrderItemOrderIdAndProductId" → "products" → "Product"
336
- const prefixMatch = field.name.match(/^([a-z]+)By/i);
337
- const actualEntityName = prefixMatch
338
- ? (0, inflekt_1.singularize)((0, inflekt_1.ucFirst)(prefixMatch[1]))
339
- : relatedEntityName;
340
- // Try to extract junction table from field name
341
- // Pattern: {relatedEntities}By{JunctionTable}{Keys}
342
- // e.g., "productsByProductCategoryProductIdAndCategoryId" → "ProductCategory"
343
- // The junction table name ends where the first field key begins (identified by capital letter after lowercase)
344
- const junctionMatch = field.name.match(/By([A-Z][a-z]+(?:[A-Z][a-z]+)*?)(?:[A-Z][a-z]+Id)/);
345
- const junctionTable = junctionMatch ? junctionMatch[1] : 'Unknown';
346
- return {
347
- type: 'manyToMany',
348
- relation: {
349
- fieldName: field.name,
350
- rightTable: actualEntityName,
351
- junctionTable,
352
- type: connectionTypeName,
353
- },
354
- };
355
- }
356
- return {
357
- type: 'hasMany',
358
- relation: {
359
- fieldName: field.name,
360
- isUnique: false,
361
- referencedByTable: relatedEntityName,
362
- type: connectionTypeName,
363
- keys: [],
364
- },
365
- };
366
- }
367
- /**
368
- * Match query operations for an entity
369
- *
370
- * Looks for:
371
- * - List query: returns {PluralName}Connection (e.g., users → UsersConnection)
372
- * - Single query: returns {EntityName} with id/nodeId arg (e.g., user → User)
373
- */
374
- function matchQueryOperations(entityName, queryFields, entityToConnection) {
375
- const pluralName = (0, inflekt_1.pluralize)(entityName);
376
- const connectionTypeName = entityToConnection.get(entityName) ?? `${pluralName}Connection`;
377
- let all = null;
378
- let one = null;
379
- for (const field of queryFields) {
380
- const returnTypeName = (0, introspection_1.getBaseTypeName)(field.type);
381
- if (!returnTypeName)
382
- continue;
383
- // Match list query by return type (Connection)
384
- if (returnTypeName === connectionTypeName) {
385
- // Prefer the simple plural name, but accept any that returns the connection
386
- if (!all || field.name === (0, inflekt_1.lcFirst)(pluralName)) {
387
- all = field.name;
388
- }
389
- }
390
- // Match single query by return type (Entity) and having an id-like arg
391
- if (returnTypeName === entityName) {
392
- const hasIdArg = field.args.some((arg) => arg.name === 'id' ||
393
- arg.name === 'nodeId' ||
394
- arg.name.toLowerCase().endsWith('id'));
395
- if (hasIdArg) {
396
- // Prefer exact match (e.g., "user" for "User")
397
- if (!one || field.name === (0, inflekt_1.lcFirst)(entityName)) {
398
- one = field.name;
399
- }
400
- }
401
- }
402
- }
403
- return { all, one };
404
- }
405
- /**
406
- * Match mutation operations for an entity
407
- *
408
- * Looks for mutations named:
409
- * - create{EntityName}
410
- * - update{EntityName} or update{EntityName}ById
411
- * - delete{EntityName} or delete{EntityName}ById
412
- */
413
- function matchMutationOperations(entityName, mutationFields) {
414
- let create = null;
415
- let update = null;
416
- let del = null;
417
- const expectedCreate = `create${entityName}`;
418
- const expectedUpdate = `update${entityName}`;
419
- const expectedDelete = `delete${entityName}`;
420
- for (const field of mutationFields) {
421
- // Exact match for create
422
- if (field.name === expectedCreate) {
423
- create = field.name;
424
- }
425
- // Match update (could be updateUser or updateUserById)
426
- if (field.name === expectedUpdate ||
427
- field.name === `${expectedUpdate}ById`) {
428
- // Prefer non-ById version
429
- if (!update || field.name === expectedUpdate) {
430
- update = field.name;
431
- }
432
- }
433
- // Match delete (could be deleteUser or deleteUserById)
434
- if (field.name === expectedDelete ||
435
- field.name === `${expectedDelete}ById`) {
436
- // Prefer non-ById version
437
- if (!del || field.name === expectedDelete) {
438
- del = field.name;
439
- }
440
- }
441
- }
442
- return { create, update, delete: del };
443
- }
444
- // ============================================================================
445
- // Constraint Inference
446
- // ============================================================================
447
- /**
448
- * Infer constraints from mutation input types
449
- *
450
- * Primary key can be inferred from Update/Delete mutation input types,
451
- * which typically have an 'id' field or similar.
452
- */
453
- function inferConstraints(entityName, typeMap) {
454
- const primaryKey = [];
455
- // Try to find Update or Delete input type to extract PK
456
- const updateInputName = `Update${entityName}Input`;
457
- const deleteInputName = `Delete${entityName}Input`;
458
- const updateInput = typeMap.get(updateInputName);
459
- const deleteInput = typeMap.get(deleteInputName);
460
- const keyInputField = inferPrimaryKeyFromInputObject(updateInput) ||
461
- inferPrimaryKeyFromInputObject(deleteInput);
462
- if (keyInputField) {
463
- primaryKey.push({
464
- name: 'primary',
465
- fields: [
466
- {
467
- name: keyInputField.name,
468
- type: convertToCleanFieldType(keyInputField.type),
469
- },
470
- ],
471
- });
472
- }
473
- // If no PK found from inputs, try to find 'id' field in entity type
474
- if (primaryKey.length === 0) {
475
- const entityType = typeMap.get(entityName);
476
- if (entityType?.fields) {
477
- const idField = entityType.fields.find((f) => f.name === 'id' || f.name === 'nodeId');
478
- if (idField) {
479
- primaryKey.push({
480
- name: 'primary',
481
- fields: [
482
- {
483
- name: idField.name,
484
- type: convertToCleanFieldType(idField.type),
485
- },
486
- ],
487
- });
488
- }
489
- }
490
- }
491
- return {
492
- primaryKey,
493
- foreignKey: [], // Would need FK info to populate
494
- unique: [], // Would need constraint info to populate
495
- };
496
- }
497
- /**
498
- * Infer a single-row lookup key from an Update/Delete input object.
499
- *
500
- * Priority:
501
- * 1. Canonical keys: id, nodeId, rowId
502
- * 2. Single non-patch/non-clientMutationId scalar-ish field
503
- *
504
- * If multiple possible key fields remain, return null to avoid guessing.
505
- */
506
- function inferPrimaryKeyFromInputObject(inputType) {
507
- const inputFields = inputType?.inputFields ?? [];
508
- if (inputFields.length === 0)
509
- return null;
510
- const canonicalKey = inputFields.find((field) => field.name === 'id' || field.name === 'nodeId' || field.name === 'rowId');
511
- if (canonicalKey)
512
- return canonicalKey;
513
- const candidates = inputFields.filter((field) => {
514
- if (field.name === 'clientMutationId')
515
- return false;
516
- const baseTypeName = (0, introspection_1.getBaseTypeName)(field.type);
517
- const lowerName = field.name.toLowerCase();
518
- // Exclude patch payload fields (patch / fooPatch)
519
- if (lowerName === 'patch' || lowerName.endsWith('patch'))
520
- return false;
521
- if (baseTypeName?.endsWith('Patch'))
522
- return false;
523
- return true;
524
- });
525
- return candidates.length === 1 ? candidates[0] : null;
526
- }
527
- /**
528
- * Infer the patch field name from an Update input type.
529
- *
530
- * PostGraphile v5 uses entity-specific patch field names:
531
- * UpdateUserInput → { id, userPatch: UserPatch }
532
- * UpdateDatabaseInput → { id, databasePatch: DatabasePatch }
533
- *
534
- * Pattern: {lcFirst(entityTypeName)}Patch
535
- */
536
- function inferPatchFieldName(entityName, typeMap) {
537
- const updateInputName = `Update${entityName}Input`;
538
- const updateInput = typeMap.get(updateInputName);
539
- const inputFields = updateInput?.inputFields ?? [];
540
- // Find the field whose type name ends in 'Patch'
541
- const patchField = inputFields.find((f) => {
542
- const baseName = (0, introspection_1.getBaseTypeName)(f.type);
543
- return baseName?.endsWith('Patch');
544
- });
545
- if (patchField)
546
- return patchField.name;
547
- // Fallback: compute from entity name (v5 default inflection)
548
- return (0, inflekt_1.lcFirst)(entityName) + 'Patch';
549
- }
550
- // ============================================================================
551
- // Inflection Building
552
- // ============================================================================
553
- /**
554
- * Build inflection map from discovered types
555
- */
556
- function buildInflection(entityName, typeMap, entityToConnection) {
557
- const pluralName = (0, inflekt_1.pluralize)(entityName);
558
- const singularFieldName = (0, inflekt_1.lcFirst)(entityName);
559
- const pluralFieldName = (0, inflekt_1.lcFirst)(pluralName);
560
- const connectionTypeName = entityToConnection.get(entityName) ?? `${pluralName}Connection`;
561
- // Check which types actually exist in the schema
562
- const hasFilter = typeMap.has(`${entityName}Filter`);
563
- const hasPatch = typeMap.has(`${entityName}Patch`);
564
- const hasUpdatePayload = typeMap.has(`Update${entityName}Payload`);
565
- // Detect the actual OrderBy type from schema
566
- // PostGraphile typically generates {PluralName}OrderBy (e.g., AddressesOrderBy)
567
- // but we check for the actual type in case of custom inflection
568
- const expectedOrderByType = `${pluralName}OrderBy`;
569
- const orderByType = findOrderByType(entityName, pluralName, typeMap) || expectedOrderByType;
570
- return {
571
- allRows: pluralFieldName,
572
- allRowsSimple: pluralFieldName,
573
- conditionType: `${entityName}Condition`,
574
- connection: connectionTypeName,
575
- createField: `create${entityName}`,
576
- createInputType: `Create${entityName}Input`,
577
- createPayloadType: `Create${entityName}Payload`,
578
- deleteByPrimaryKey: `delete${entityName}`,
579
- deletePayloadType: `Delete${entityName}Payload`,
580
- edge: `${pluralName}Edge`,
581
- edgeField: (0, inflekt_1.lcFirst)(pluralName),
582
- enumType: `${entityName}Enum`,
583
- filterType: hasFilter ? `${entityName}Filter` : null,
584
- inputType: `${entityName}Input`,
585
- orderByType,
586
- patchField: singularFieldName,
587
- patchType: hasPatch ? `${entityName}Patch` : null,
588
- tableFieldName: singularFieldName,
589
- tableType: entityName,
590
- typeName: entityName,
591
- updateByPrimaryKey: `update${entityName}`,
592
- updatePayloadType: hasUpdatePayload ? `Update${entityName}Payload` : null,
593
- };
594
- }
595
- /**
596
- * Find the actual OrderBy enum type for an entity from the schema
597
- *
598
- * PostGraphile generates OrderBy enums with various patterns:
599
- * - {PluralName}OrderBy (e.g., AddressesOrderBy, UsersOrderBy)
600
- * - Sometimes with custom inflection (e.g., SchemataOrderBy for Schema)
601
- *
602
- * We search for the actual type in the schema to handle all cases.
603
- */
604
- function findOrderByType(entityName, pluralName, typeMap) {
605
- // Try the standard pattern first: {PluralName}OrderBy
606
- const standardName = `${pluralName}OrderBy`;
607
- if (typeMap.has(standardName)) {
608
- return standardName;
609
- }
610
- // Build a list of candidate OrderBy names to check
611
- // These are variations of the entity name with common plural suffixes
612
- const candidates = [
613
- `${entityName}sOrderBy`, // Simple 's' plural: User -> UsersOrderBy
614
- `${entityName}esOrderBy`, // 'es' plural: Address -> AddressesOrderBy
615
- `${entityName}OrderBy`, // No change (already plural or singular OK)
616
- ];
617
- // Check each candidate
618
- for (const candidate of candidates) {
619
- if (typeMap.has(candidate) && typeMap.get(candidate)?.kind === 'ENUM') {
620
- return candidate;
621
- }
622
- }
623
- // Fallback: search for an enum that EXACTLY matches the entity plural pattern
624
- // We look for types that are the pluralized entity name + OrderBy
625
- // This avoids matching SchemaGrantsOrderBy when looking for Schema's OrderBy
626
- for (const [typeName, type] of typeMap) {
627
- if (type.kind !== 'ENUM' || !typeName.endsWith('OrderBy'))
628
- continue;
629
- // Extract the base name (without OrderBy suffix)
630
- const baseName = typeName.slice(0, -7); // Remove 'OrderBy'
631
- // Check if singularizing the base name gives us the entity name
632
- // e.g., 'Schemata' -> 'Schema', 'Users' -> 'User', 'Addresses' -> 'Address'
633
- if ((0, inflekt_1.singularize)(baseName) === entityName) {
634
- return typeName;
635
- }
636
- }
637
- return null;
638
- }
639
- // ============================================================================
640
- // Utility Functions
641
- // ============================================================================
642
- /**
643
- * Build a map of type name → IntrospectionType for efficient lookup
644
- */
645
- function buildTypeMap(types) {
646
- const map = new Map();
647
- for (const type of types) {
648
- map.set(type.name, type);
649
- }
650
- return map;
651
- }
652
- /**
653
- * Get fields from a type, returning empty array if null
654
- */
655
- function getTypeFields(type) {
656
- return type?.fields ?? [];
657
- }
7
+ var graphql_query_1 = require("@constructive-io/graphql-query");
8
+ Object.defineProperty(exports, "inferTablesFromIntrospection", { enumerable: true, get: function () { return graphql_query_1.inferTablesFromIntrospection; } });
@@ -1,20 +1,5 @@
1
1
  /**
2
- * GraphQL Schema Introspection Query
3
- *
4
- * Full introspection query that captures all queries, mutations, and types
5
- * from a GraphQL endpoint via the standard __schema query.
2
+ * Re-export schema query from @constructive-io/graphql-query.
6
3
  */
7
- import type { IntrospectionQueryResponse } from '../../types/introspection';
8
- /**
9
- * Full schema introspection query
10
- *
11
- * Captures:
12
- * - All Query fields with args and return types
13
- * - All Mutation fields with args and return types
14
- * - All types (OBJECT, INPUT_OBJECT, ENUM, SCALAR) for resolution
15
- *
16
- * Uses a recursive TypeRef fragment to handle deeply nested type wrappers
17
- * (e.g., [String!]! = NON_NULL(LIST(NON_NULL(SCALAR))))
18
- */
19
- export declare const SCHEMA_INTROSPECTION_QUERY = "\nquery IntrospectSchema {\n __schema {\n queryType {\n name\n }\n mutationType {\n name\n }\n subscriptionType {\n name\n }\n types {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n description\n args {\n name\n description\n type {\n ...TypeRef\n }\n defaultValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n name\n description\n type {\n ...TypeRef\n }\n defaultValue\n }\n interfaces {\n name\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n name\n }\n }\n directives {\n name\n description\n locations\n args {\n name\n description\n type {\n ...TypeRef\n }\n defaultValue\n }\n }\n }\n}\n\nfragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n}\n";
20
- export type { IntrospectionQueryResponse };
4
+ export { SCHEMA_INTROSPECTION_QUERY } from '@constructive-io/graphql-query';
5
+ export type { IntrospectionQueryResponse } from '@constructive-io/graphql-query';