@wundergraph/protographic 0.1.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.
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Class to manage proto lock data for deterministic field ordering
3
+ */
4
+ export class ProtoLockManager {
5
+ /**
6
+ * Create a new ProtoLockManager
7
+ *
8
+ * @param initialLockData - Initial lock data to use, if any
9
+ */
10
+ constructor(initialLockData) {
11
+ this.lockData = initialLockData || {
12
+ version: '1.0.0',
13
+ messages: {},
14
+ enums: {},
15
+ };
16
+ }
17
+ /**
18
+ * Generic method to reconcile items and their numbers
19
+ *
20
+ * @param container - The container object (messages or enums)
21
+ * @param itemName - Name of the item (message or enum)
22
+ * @param availableItems - Available item names (fields or enum values)
23
+ * @returns Ordered array of item names
24
+ */
25
+ reconcileItems(container, itemName, availableItems) {
26
+ // Ensure container item exists
27
+ if (!container[itemName]) {
28
+ container[itemName] = { fields: {} };
29
+ }
30
+ // Get existing fields map
31
+ const fieldsMap = container[itemName].fields;
32
+ // Get existing reserved numbers
33
+ let reservedNumbers = container[itemName].reservedNumbers || [];
34
+ // Track removed items and their numbers
35
+ const removedItems = {};
36
+ // Identify removed items
37
+ Object.entries(fieldsMap).forEach(([item, number]) => {
38
+ if (!availableItems.includes(item)) {
39
+ reservedNumbers.push(number);
40
+ removedItems[item] = number;
41
+ }
42
+ });
43
+ // Deduplicate reserved numbers
44
+ reservedNumbers = [...new Set(reservedNumbers)];
45
+ // Create new fields map
46
+ const newFieldsMap = {};
47
+ // Preserve existing numbers for items that are still available
48
+ availableItems.forEach((item) => {
49
+ const existingNumber = fieldsMap[item];
50
+ if (existingNumber !== undefined) {
51
+ newFieldsMap[item] = existingNumber;
52
+ // Remove from reserved if it's reused
53
+ const index = reservedNumbers.indexOf(existingNumber);
54
+ if (index !== -1) {
55
+ reservedNumbers.splice(index, 1);
56
+ }
57
+ }
58
+ });
59
+ // Get highest assigned number
60
+ let maxNumber = 0;
61
+ Object.values(newFieldsMap).forEach((num) => {
62
+ maxNumber = Math.max(maxNumber, num);
63
+ });
64
+ // Also consider reserved numbers for max
65
+ if (reservedNumbers.length > 0) {
66
+ maxNumber = Math.max(maxNumber, ...reservedNumbers);
67
+ }
68
+ // Assign numbers to items that don't have one
69
+ availableItems.forEach((item) => {
70
+ if (newFieldsMap[item] === undefined) {
71
+ // Check if the item was previously removed (exists in our reservedNumbers)
72
+ let reservedNumber;
73
+ Object.entries(removedItems).forEach(([removedItem, number]) => {
74
+ if (removedItem === item && reservedNumbers.includes(number)) {
75
+ reservedNumber = number;
76
+ }
77
+ });
78
+ if (reservedNumber !== undefined) {
79
+ // Reuse the reserved number for this item
80
+ newFieldsMap[item] = reservedNumber;
81
+ // Remove from reserved list
82
+ const index = reservedNumbers.indexOf(reservedNumber);
83
+ if (index !== -1) {
84
+ reservedNumbers.splice(index, 1);
85
+ }
86
+ }
87
+ else {
88
+ // Find next available number
89
+ let nextNumber = maxNumber + 1;
90
+ while (reservedNumbers.includes(nextNumber)) {
91
+ nextNumber++;
92
+ }
93
+ newFieldsMap[item] = nextNumber;
94
+ maxNumber = nextNumber;
95
+ }
96
+ }
97
+ });
98
+ // Update the fields map and reserved numbers
99
+ container[itemName].fields = newFieldsMap;
100
+ if (reservedNumbers.length > 0) {
101
+ container[itemName].reservedNumbers = reservedNumbers;
102
+ }
103
+ else {
104
+ // If no reserved numbers, make sure the property doesn't exist
105
+ delete container[itemName].reservedNumbers;
106
+ }
107
+ // Sort available items by their assigned numbers
108
+ return [...availableItems].sort((a, b) => {
109
+ return newFieldsMap[a] - newFieldsMap[b];
110
+ });
111
+ }
112
+ /**
113
+ * Reconcile and get the ordered field names for a message
114
+ *
115
+ * @param messageName - Name of the message
116
+ * @param availableFields - Available field names (used when no lock exists)
117
+ * @returns Ordered array of field names
118
+ */
119
+ reconcileMessageFieldOrder(messageName, availableFields) {
120
+ return this.reconcileItems(this.lockData.messages, messageName, availableFields);
121
+ }
122
+ /**
123
+ * Reconcile and get the ordered enum values
124
+ *
125
+ * @param enumName - Name of the enum
126
+ * @param availableValues - Available enum values (used when no lock exists)
127
+ * @returns Ordered array of enum values
128
+ */
129
+ reconcileEnumValueOrder(enumName, availableValues) {
130
+ return this.reconcileItems(this.lockData.enums, enumName, availableValues);
131
+ }
132
+ /**
133
+ * Reconcile and get the ordered argument names for a field
134
+ *
135
+ * @param fieldPath - Path to the field in the format "TypeName.fieldName"
136
+ * @param availableArgs - Available argument names (used when no lock exists)
137
+ * @returns Ordered array of argument names
138
+ */
139
+ reconcileArgumentOrder(fieldPath, availableArgs) {
140
+ // Use the regular message field ordering for arguments
141
+ return this.reconcileMessageFieldOrder(fieldPath, availableArgs);
142
+ }
143
+ /**
144
+ * Get the current lock data
145
+ */
146
+ getLockData() {
147
+ return this.lockData;
148
+ }
149
+ }
150
+ //# sourceMappingURL=proto-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proto-lock.js","sourceRoot":"","sources":["../../src/proto-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ import { GraphQLSchema } from 'graphql';
2
+ import { GRPCMapping } from '@wundergraph/cosmo-connect/dist/node/v1/node_pb';
3
+ /**
4
+ * Visitor that converts a GraphQL schema to gRPC mapping definitions
5
+ *
6
+ * This visitor traverses a GraphQL schema and generates mappings between:
7
+ * - GraphQL operations and gRPC RPC methods
8
+ * - GraphQL entity types (with @key directive) and corresponding lookup methods
9
+ * - GraphQL types/fields and Protocol Buffer message types/fields
10
+ * - GraphQL enums and Protocol Buffer enums
11
+ *
12
+ * The generated mappings are used to translate between GraphQL and Protocol Buffer
13
+ * representations in a consistent manner.
14
+ */
15
+ export declare class GraphQLToProtoVisitor {
16
+ private readonly mapping;
17
+ private readonly schema;
18
+ /**
19
+ * Creates a new visitor for generating gRPC mappings from a GraphQL schema
20
+ *
21
+ * @param schema - The GraphQL schema to process
22
+ * @param serviceName - Name for the generated service (defaults to "DefaultService")
23
+ */
24
+ constructor(schema: GraphQLSchema, serviceName?: string);
25
+ /**
26
+ * Process the GraphQL schema and generate all necessary mappings
27
+ *
28
+ * The processing order is important:
29
+ * 1. First entity types (with @key directives) are processed to identify federated entities
30
+ * 2. Then Query operations are processed to map GraphQL queries to RPC methods
31
+ * 3. Finally all remaining types are processed to ensure complete mapping coverage
32
+ *
33
+ * @returns The completed gRPC mapping definitions
34
+ */
35
+ visit(): GRPCMapping;
36
+ /**
37
+ * Processes entity types (GraphQL types with @key directive)
38
+ *
39
+ * Federation entities require special handling to generate appropriate
40
+ * lookup RPC methods and entity mappings.
41
+ */
42
+ private processEntityTypes;
43
+ /**
44
+ * Extract the key directive from a GraphQL object type
45
+ *
46
+ * @param type - The GraphQL object type to check for key directive
47
+ * @returns The key directive if found, undefined otherwise
48
+ */
49
+ private getKeyDirective;
50
+ /**
51
+ * Creates an entity mapping for a federated entity type
52
+ *
53
+ * This defines how a GraphQL federated entity maps to a gRPC lookup method
54
+ * and its corresponding request/response messages.
55
+ *
56
+ * @param typeName - The name of the GraphQL entity type
57
+ * @param keyField - The field that serves as the entity's key
58
+ */
59
+ private createEntityMapping;
60
+ /**
61
+ * Extract key fields from a @key directive
62
+ *
63
+ * The @key directive specifies which fields form the entity's primary key
64
+ * in Federation. This method extracts those field names.
65
+ *
66
+ * @param directive - The @key directive from the GraphQL AST
67
+ * @returns Array of field names that form the key
68
+ */
69
+ private getKeyFieldsFromDirective;
70
+ /**
71
+ * Process the GraphQL Query type to generate query operation mappings
72
+ *
73
+ * Each field on the Query type represents a GraphQL query operation that
74
+ * needs to be mapped to a corresponding gRPC RPC method.
75
+ */
76
+ private processQueryType;
77
+ /**
78
+ * Process the GraphQL Mutation type to generate mutation operation mappings
79
+ *
80
+ * Each field on the Mutation type represents a GraphQL mutation operation that
81
+ * needs to be mapped to a corresponding gRPC RPC method.
82
+ */
83
+ private processMutationType;
84
+ /**
85
+ * Process the GraphQL Subscription type to generate subscription operation mappings
86
+ *
87
+ * Each field on the Subscription type represents a GraphQL subscription operation that
88
+ * needs to be mapped to a corresponding gRPC RPC method.
89
+ */
90
+ private processSubscriptionType;
91
+ /**
92
+ * Process a GraphQL type to generate operation mappings
93
+ *
94
+ * This method processes a specific GraphQL type (e.g., Query, Mutation, Subscription)
95
+ * and generates mappings for its fields to corresponding gRPC RPC methods.
96
+ *
97
+ * @param operationTypeName - The name of the GraphQL type (Query, Mutation, Subscription)
98
+ * @param operationType - The type of operation (Query, Mutation, Subscription)
99
+ * @param graphqlType - The GraphQL type to process
100
+ */
101
+ private processType;
102
+ /**
103
+ * Create an operation mapping between a GraphQL query and gRPC method
104
+ *
105
+ * @param operationType - The type of operation (Query, Mutation, Subscription)
106
+ * @param fieldName - Original GraphQL field name
107
+ * @param mappedName - Transformed name for use in gRPC context
108
+ */
109
+ private createOperationMapping;
110
+ /**
111
+ * Process all remaining GraphQL types to generate complete mappings
112
+ *
113
+ * This ensures that all object types, input types, and enums in the schema
114
+ * have appropriate mappings for their fields and values.
115
+ */
116
+ private processAllTypes;
117
+ /**
118
+ * Determines if a type should be skipped during processing
119
+ *
120
+ * We skip:
121
+ * - Built-in GraphQL types (prefixed with __)
122
+ * - Root operation types (Query, Mutation, Subscription)
123
+ *
124
+ * @param type - The GraphQL type to check
125
+ * @returns True if the type should be skipped, false otherwise
126
+ */
127
+ private shouldSkipRootType;
128
+ /**
129
+ * Process a GraphQL object type to generate field mappings
130
+ *
131
+ * @param type - The GraphQL object type to process
132
+ */
133
+ private processObjectType;
134
+ /**
135
+ * Process a GraphQL input object type to generate field mappings
136
+ *
137
+ * Input objects are handled separately because they have different
138
+ * field structures than regular object types.
139
+ *
140
+ * @param type - The GraphQL input object type to process
141
+ */
142
+ private processInputObjectType;
143
+ /**
144
+ * Process a GraphQL enum type to generate value mappings
145
+ *
146
+ * GraphQL enums are mapped to Protocol Buffer enums with appropriate
147
+ * naming conventions for the enum values.
148
+ *
149
+ * @param type - The GraphQL enum type to process
150
+ */
151
+ private processEnumType;
152
+ /**
153
+ * Create a field mapping between a GraphQL field and Protocol Buffer field
154
+ *
155
+ * This includes mapping the field name and any arguments the field may have.
156
+ *
157
+ * @param type - The name of the containing GraphQL type
158
+ * @param field - The GraphQL field to create a mapping for
159
+ * @returns The created field mapping
160
+ */
161
+ private createFieldMapping;
162
+ /**
163
+ * Create argument mappings for a GraphQL field
164
+ *
165
+ * Maps each argument to its Protocol Buffer representation with
166
+ * appropriate naming conventions.
167
+ *
168
+ * @param field - The GraphQL field containing arguments
169
+ * @returns Array of argument mappings
170
+ */
171
+ private createArgumentMappings;
172
+ }
@@ -0,0 +1,365 @@
1
+ import { isEnumType, isInputObjectType, isObjectType, Kind, } from 'graphql';
2
+ import { createEntityLookupMethodName, createEntityLookupRequestName, createEntityLookupResponseName, createOperationMethodName, createRequestMessageName, createResponseMessageName, graphqlArgumentToProtoField, graphqlEnumValueToProtoEnumValue, graphqlFieldToProtoField, } from './naming-conventions.js';
3
+ import { ArgumentMapping, EntityMapping, EnumMapping, EnumValueMapping, FieldMapping, GRPCMapping, OperationMapping, OperationType, TypeFieldMapping, } from '@wundergraph/cosmo-connect/dist/node/v1/node_pb';
4
+ /**
5
+ * Visitor that converts a GraphQL schema to gRPC mapping definitions
6
+ *
7
+ * This visitor traverses a GraphQL schema and generates mappings between:
8
+ * - GraphQL operations and gRPC RPC methods
9
+ * - GraphQL entity types (with @key directive) and corresponding lookup methods
10
+ * - GraphQL types/fields and Protocol Buffer message types/fields
11
+ * - GraphQL enums and Protocol Buffer enums
12
+ *
13
+ * The generated mappings are used to translate between GraphQL and Protocol Buffer
14
+ * representations in a consistent manner.
15
+ */
16
+ export class GraphQLToProtoVisitor {
17
+ /**
18
+ * Creates a new visitor for generating gRPC mappings from a GraphQL schema
19
+ *
20
+ * @param schema - The GraphQL schema to process
21
+ * @param serviceName - Name for the generated service (defaults to "DefaultService")
22
+ */
23
+ constructor(schema, serviceName = 'DefaultService') {
24
+ this.schema = schema;
25
+ this.mapping = new GRPCMapping({
26
+ version: 1,
27
+ service: serviceName,
28
+ operationMappings: [],
29
+ entityMappings: [],
30
+ typeFieldMappings: [],
31
+ enumMappings: [],
32
+ });
33
+ }
34
+ /**
35
+ * Process the GraphQL schema and generate all necessary mappings
36
+ *
37
+ * The processing order is important:
38
+ * 1. First entity types (with @key directives) are processed to identify federated entities
39
+ * 2. Then Query operations are processed to map GraphQL queries to RPC methods
40
+ * 3. Finally all remaining types are processed to ensure complete mapping coverage
41
+ *
42
+ * @returns The completed gRPC mapping definitions
43
+ */
44
+ visit() {
45
+ // Process entity types first (types with @key directive)
46
+ this.processEntityTypes();
47
+ // Process query type
48
+ this.processQueryType();
49
+ // Process mutation type
50
+ this.processMutationType();
51
+ // Process subscription type
52
+ this.processSubscriptionType();
53
+ // Process all other types for field mappings
54
+ this.processAllTypes();
55
+ return this.mapping;
56
+ }
57
+ /**
58
+ * Processes entity types (GraphQL types with @key directive)
59
+ *
60
+ * Federation entities require special handling to generate appropriate
61
+ * lookup RPC methods and entity mappings.
62
+ */
63
+ processEntityTypes() {
64
+ const typeMap = this.schema.getTypeMap();
65
+ for (const typeName in typeMap) {
66
+ const type = typeMap[typeName];
67
+ // Skip built-in types and query/mutation/subscription types
68
+ if (this.shouldSkipRootType(type))
69
+ continue;
70
+ // Check if this is an entity type (has @key directive)
71
+ if (isObjectType(type)) {
72
+ const keyDirective = this.getKeyDirective(type);
73
+ if (!keyDirective)
74
+ continue;
75
+ const keyFields = this.getKeyFieldsFromDirective(keyDirective);
76
+ if (keyFields.length > 0) {
77
+ // Create entity mapping using the first key field
78
+ this.createEntityMapping(typeName, keyFields[0]);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Extract the key directive from a GraphQL object type
85
+ *
86
+ * @param type - The GraphQL object type to check for key directive
87
+ * @returns The key directive if found, undefined otherwise
88
+ */
89
+ getKeyDirective(type) {
90
+ var _a, _b;
91
+ return (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.directives) === null || _b === void 0 ? void 0 : _b.find((d) => d.name.value === 'key');
92
+ }
93
+ /**
94
+ * Creates an entity mapping for a federated entity type
95
+ *
96
+ * This defines how a GraphQL federated entity maps to a gRPC lookup method
97
+ * and its corresponding request/response messages.
98
+ *
99
+ * @param typeName - The name of the GraphQL entity type
100
+ * @param keyField - The field that serves as the entity's key
101
+ */
102
+ createEntityMapping(typeName, keyField) {
103
+ const entityMapping = new EntityMapping({
104
+ typeName,
105
+ kind: 'entity',
106
+ key: keyField,
107
+ rpc: createEntityLookupMethodName(typeName, keyField),
108
+ request: createEntityLookupRequestName(typeName, keyField),
109
+ response: createEntityLookupResponseName(typeName, keyField),
110
+ });
111
+ this.mapping.entityMappings.push(entityMapping);
112
+ }
113
+ /**
114
+ * Extract key fields from a @key directive
115
+ *
116
+ * The @key directive specifies which fields form the entity's primary key
117
+ * in Federation. This method extracts those field names.
118
+ *
119
+ * @param directive - The @key directive from the GraphQL AST
120
+ * @returns Array of field names that form the key
121
+ */
122
+ getKeyFieldsFromDirective(directive) {
123
+ var _a;
124
+ // Extract fields argument from the key directive
125
+ const fieldsArg = (_a = directive.arguments) === null || _a === void 0 ? void 0 : _a.find((arg) => arg.name.value === 'fields');
126
+ if (fieldsArg && fieldsArg.value.kind === Kind.STRING) {
127
+ return fieldsArg.value.value.split(' ');
128
+ }
129
+ return [];
130
+ }
131
+ /**
132
+ * Process the GraphQL Query type to generate query operation mappings
133
+ *
134
+ * Each field on the Query type represents a GraphQL query operation that
135
+ * needs to be mapped to a corresponding gRPC RPC method.
136
+ */
137
+ processQueryType() {
138
+ this.processType('Query', OperationType.QUERY, this.schema.getQueryType());
139
+ }
140
+ /**
141
+ * Process the GraphQL Mutation type to generate mutation operation mappings
142
+ *
143
+ * Each field on the Mutation type represents a GraphQL mutation operation that
144
+ * needs to be mapped to a corresponding gRPC RPC method.
145
+ */
146
+ processMutationType() {
147
+ this.processType('Mutation', OperationType.MUTATION, this.schema.getMutationType());
148
+ }
149
+ /**
150
+ * Process the GraphQL Subscription type to generate subscription operation mappings
151
+ *
152
+ * Each field on the Subscription type represents a GraphQL subscription operation that
153
+ * needs to be mapped to a corresponding gRPC RPC method.
154
+ */
155
+ processSubscriptionType() {
156
+ this.processType('Subscription', OperationType.SUBSCRIPTION, this.schema.getSubscriptionType());
157
+ }
158
+ /**
159
+ * Process a GraphQL type to generate operation mappings
160
+ *
161
+ * This method processes a specific GraphQL type (e.g., Query, Mutation, Subscription)
162
+ * and generates mappings for its fields to corresponding gRPC RPC methods.
163
+ *
164
+ * @param operationTypeName - The name of the GraphQL type (Query, Mutation, Subscription)
165
+ * @param operationType - The type of operation (Query, Mutation, Subscription)
166
+ * @param graphqlType - The GraphQL type to process
167
+ */
168
+ processType(operationTypeName, operationType, graphqlType) {
169
+ if (!graphqlType)
170
+ return;
171
+ const typeFieldMapping = new TypeFieldMapping({
172
+ type: operationTypeName,
173
+ fieldMappings: [],
174
+ });
175
+ const fields = graphqlType.getFields();
176
+ for (const fieldName in fields) {
177
+ // Skip special federation fields
178
+ if (fieldName === '_entities')
179
+ continue;
180
+ const field = fields[fieldName];
181
+ const mappedName = createOperationMethodName(operationTypeName, fieldName);
182
+ this.createOperationMapping(operationType, fieldName, mappedName);
183
+ const fieldMapping = this.createFieldMapping(operationTypeName, field);
184
+ typeFieldMapping.fieldMappings.push(fieldMapping);
185
+ }
186
+ this.mapping.typeFieldMappings.push(typeFieldMapping);
187
+ }
188
+ /**
189
+ * Create an operation mapping between a GraphQL query and gRPC method
190
+ *
191
+ * @param operationType - The type of operation (Query, Mutation, Subscription)
192
+ * @param fieldName - Original GraphQL field name
193
+ * @param mappedName - Transformed name for use in gRPC context
194
+ */
195
+ createOperationMapping(operationType, fieldName, mappedName) {
196
+ const operationMapping = new OperationMapping({
197
+ type: operationType,
198
+ original: fieldName,
199
+ mapped: mappedName,
200
+ request: createRequestMessageName(mappedName),
201
+ response: createResponseMessageName(mappedName),
202
+ });
203
+ this.mapping.operationMappings.push(operationMapping);
204
+ }
205
+ /**
206
+ * Process all remaining GraphQL types to generate complete mappings
207
+ *
208
+ * This ensures that all object types, input types, and enums in the schema
209
+ * have appropriate mappings for their fields and values.
210
+ */
211
+ processAllTypes() {
212
+ const typeMap = this.schema.getTypeMap();
213
+ for (const typeName in typeMap) {
214
+ const type = typeMap[typeName];
215
+ if (this.shouldSkipRootType(type))
216
+ continue;
217
+ // Process each type according to its kind
218
+ if (isObjectType(type)) {
219
+ this.processObjectType(type);
220
+ }
221
+ else if (isInputObjectType(type)) {
222
+ this.processInputObjectType(type);
223
+ }
224
+ else if (isEnumType(type)) {
225
+ this.processEnumType(type);
226
+ }
227
+ // Note: Union types don't need field mappings in our implementation
228
+ }
229
+ }
230
+ /**
231
+ * Determines if a type should be skipped during processing
232
+ *
233
+ * We skip:
234
+ * - Built-in GraphQL types (prefixed with __)
235
+ * - Root operation types (Query, Mutation, Subscription)
236
+ *
237
+ * @param type - The GraphQL type to check
238
+ * @returns True if the type should be skipped, false otherwise
239
+ */
240
+ shouldSkipRootType(type) {
241
+ var _a, _b, _c;
242
+ const typeName = type.name;
243
+ return (typeName.startsWith('__') ||
244
+ typeName === ((_a = this.schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name) ||
245
+ typeName === ((_b = this.schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name) ||
246
+ typeName === ((_c = this.schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name));
247
+ }
248
+ /**
249
+ * Process a GraphQL object type to generate field mappings
250
+ *
251
+ * @param type - The GraphQL object type to process
252
+ */
253
+ processObjectType(type) {
254
+ const typeFieldMapping = new TypeFieldMapping({
255
+ type: type.name,
256
+ fieldMappings: [],
257
+ });
258
+ const fields = type.getFields();
259
+ for (const fieldName in fields) {
260
+ const field = fields[fieldName];
261
+ const fieldMapping = this.createFieldMapping(type.name, field);
262
+ typeFieldMapping.fieldMappings.push(fieldMapping);
263
+ }
264
+ // Only add to mappings if there are fields to map
265
+ if (typeFieldMapping.fieldMappings.length > 0) {
266
+ this.mapping.typeFieldMappings.push(typeFieldMapping);
267
+ }
268
+ }
269
+ /**
270
+ * Process a GraphQL input object type to generate field mappings
271
+ *
272
+ * Input objects are handled separately because they have different
273
+ * field structures than regular object types.
274
+ *
275
+ * @param type - The GraphQL input object type to process
276
+ */
277
+ processInputObjectType(type) {
278
+ const typeFieldMapping = new TypeFieldMapping({
279
+ type: type.name,
280
+ fieldMappings: [],
281
+ });
282
+ const fields = type.getFields();
283
+ for (const fieldName in fields) {
284
+ const field = fields[fieldName];
285
+ // Input fields don't have args, so we create a simpler field mapping
286
+ const fieldMapping = new FieldMapping({
287
+ original: field.name,
288
+ mapped: graphqlFieldToProtoField(field.name),
289
+ argumentMappings: [],
290
+ });
291
+ typeFieldMapping.fieldMappings.push(fieldMapping);
292
+ }
293
+ // Only add to mappings if there are fields to map
294
+ if (typeFieldMapping.fieldMappings.length > 0) {
295
+ this.mapping.typeFieldMappings.push(typeFieldMapping);
296
+ }
297
+ }
298
+ /**
299
+ * Process a GraphQL enum type to generate value mappings
300
+ *
301
+ * GraphQL enums are mapped to Protocol Buffer enums with appropriate
302
+ * naming conventions for the enum values.
303
+ *
304
+ * @param type - The GraphQL enum type to process
305
+ */
306
+ processEnumType(type) {
307
+ const enumMapping = new EnumMapping({
308
+ type: type.name,
309
+ values: [],
310
+ });
311
+ const enumValues = type.getValues();
312
+ // Map each enum value to its Protocol Buffer representation
313
+ for (const enumValue of enumValues) {
314
+ enumMapping.values.push(new EnumValueMapping({
315
+ original: enumValue.name,
316
+ // Convert to UPPER_SNAKE_CASE with type name prefix for Proto enums
317
+ mapped: graphqlEnumValueToProtoEnumValue(type.name, enumValue.name),
318
+ }));
319
+ }
320
+ this.mapping.enumMappings.push(enumMapping);
321
+ }
322
+ /**
323
+ * Create a field mapping between a GraphQL field and Protocol Buffer field
324
+ *
325
+ * This includes mapping the field name and any arguments the field may have.
326
+ *
327
+ * @param type - The name of the containing GraphQL type
328
+ * @param field - The GraphQL field to create a mapping for
329
+ * @returns The created field mapping
330
+ */
331
+ createFieldMapping(type, field) {
332
+ const fieldName = field.name;
333
+ // Convert field names to snake_case for Protocol Buffers
334
+ const mappedFieldName = graphqlFieldToProtoField(fieldName);
335
+ const argumentMappings = this.createArgumentMappings(field);
336
+ return new FieldMapping({
337
+ original: fieldName,
338
+ mapped: mappedFieldName,
339
+ argumentMappings,
340
+ });
341
+ }
342
+ /**
343
+ * Create argument mappings for a GraphQL field
344
+ *
345
+ * Maps each argument to its Protocol Buffer representation with
346
+ * appropriate naming conventions.
347
+ *
348
+ * @param field - The GraphQL field containing arguments
349
+ * @returns Array of argument mappings
350
+ */
351
+ createArgumentMappings(field) {
352
+ const argumentMappings = [];
353
+ if (field.args && field.args.length > 0) {
354
+ for (const arg of field.args) {
355
+ argumentMappings.push(new ArgumentMapping({
356
+ original: arg.name,
357
+ // Convert argument names to snake_case for Protocol Buffers
358
+ mapped: graphqlArgumentToProtoField(arg.name),
359
+ }));
360
+ }
361
+ }
362
+ return argumentMappings;
363
+ }
364
+ }
365
+ //# sourceMappingURL=sdl-to-mapping-visitor.js.map