@wundergraph/protographic 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -3
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/operation-to-proto.d.ts +48 -0
- package/dist/src/operation-to-proto.js +378 -0
- package/dist/src/operation-to-proto.js.map +1 -0
- package/dist/src/operations/field-numbering.d.ts +73 -0
- package/dist/src/operations/field-numbering.js +134 -0
- package/dist/src/operations/field-numbering.js.map +1 -0
- package/dist/src/operations/list-type-utils.d.ts +28 -0
- package/dist/src/operations/list-type-utils.js +49 -0
- package/dist/src/operations/list-type-utils.js.map +1 -0
- package/dist/src/operations/message-builder.d.ts +58 -0
- package/dist/src/operations/message-builder.js +377 -0
- package/dist/src/operations/message-builder.js.map +1 -0
- package/dist/src/operations/proto-text-generator.d.ts +74 -0
- package/dist/src/operations/proto-text-generator.js +336 -0
- package/dist/src/operations/proto-text-generator.js.map +1 -0
- package/dist/src/operations/request-builder.d.ts +56 -0
- package/dist/src/operations/request-builder.js +194 -0
- package/dist/src/operations/request-builder.js.map +1 -0
- package/dist/src/operations/type-mapper.d.ts +66 -0
- package/dist/src/operations/type-mapper.js +236 -0
- package/dist/src/operations/type-mapper.js.map +1 -0
- package/dist/src/proto-options.d.ts +23 -0
- package/dist/src/proto-options.js +45 -0
- package/dist/src/proto-options.js.map +1 -0
- package/dist/src/sdl-to-proto-visitor.d.ts +2 -14
- package/dist/src/sdl-to-proto-visitor.js +25 -38
- package/dist/src/sdl-to-proto-visitor.js.map +1 -1
- package/dist/src/types.d.ts +12 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new field number manager instance
|
|
3
|
+
*
|
|
4
|
+
* @param lockManager - Optional ProtoLockManager for field number stability
|
|
5
|
+
* @returns A new field number manager
|
|
6
|
+
*/
|
|
7
|
+
export function createFieldNumberManager(lockManager) {
|
|
8
|
+
// Map of message name to field name to field number
|
|
9
|
+
const fieldNumbers = new Map();
|
|
10
|
+
// Map of message name to the next available field number
|
|
11
|
+
const nextFieldNumbers = new Map();
|
|
12
|
+
return {
|
|
13
|
+
getNextFieldNumber(messageName) {
|
|
14
|
+
// If we have a lock manager and this message has been reconciled,
|
|
15
|
+
// check if we already have a field number assigned
|
|
16
|
+
if (lockManager) {
|
|
17
|
+
const lockData = lockManager.getLockData();
|
|
18
|
+
const messageData = lockData.messages[messageName];
|
|
19
|
+
if (messageData) {
|
|
20
|
+
// Find the highest assigned number
|
|
21
|
+
const assignedNumbers = Object.values(messageData.fields);
|
|
22
|
+
const reservedNumbers = messageData.reservedNumbers || [];
|
|
23
|
+
const allNumbers = [...assignedNumbers, ...reservedNumbers];
|
|
24
|
+
if (allNumbers.length > 0) {
|
|
25
|
+
const maxNumber = Math.max(...allNumbers);
|
|
26
|
+
// Initialize next field number to be after the max
|
|
27
|
+
if (!nextFieldNumbers.has(messageName)) {
|
|
28
|
+
nextFieldNumbers.set(messageName, maxNumber + 1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Initialize if needed
|
|
34
|
+
if (!nextFieldNumbers.has(messageName)) {
|
|
35
|
+
nextFieldNumbers.set(messageName, 1);
|
|
36
|
+
}
|
|
37
|
+
const current = nextFieldNumbers.get(messageName);
|
|
38
|
+
nextFieldNumbers.set(messageName, current + 1);
|
|
39
|
+
return current;
|
|
40
|
+
},
|
|
41
|
+
assignFieldNumber(messageName, fieldName, fieldNumber) {
|
|
42
|
+
// Initialize message map if needed
|
|
43
|
+
if (!fieldNumbers.has(messageName)) {
|
|
44
|
+
fieldNumbers.set(messageName, new Map());
|
|
45
|
+
}
|
|
46
|
+
const messageFields = fieldNumbers.get(messageName);
|
|
47
|
+
messageFields.set(fieldName, fieldNumber);
|
|
48
|
+
// Update next field number if this assignment affects it
|
|
49
|
+
const currentNext = nextFieldNumbers.get(messageName) || 1;
|
|
50
|
+
if (fieldNumber >= currentNext) {
|
|
51
|
+
nextFieldNumbers.set(messageName, fieldNumber + 1);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
getFieldNumber(messageName, fieldName) {
|
|
55
|
+
var _a;
|
|
56
|
+
return (_a = fieldNumbers.get(messageName)) === null || _a === void 0 ? void 0 : _a.get(fieldName);
|
|
57
|
+
},
|
|
58
|
+
resetMessage(messageName) {
|
|
59
|
+
fieldNumbers.delete(messageName);
|
|
60
|
+
nextFieldNumbers.set(messageName, 1);
|
|
61
|
+
},
|
|
62
|
+
resetAll() {
|
|
63
|
+
fieldNumbers.clear();
|
|
64
|
+
nextFieldNumbers.clear();
|
|
65
|
+
},
|
|
66
|
+
getMessageFields(messageName) {
|
|
67
|
+
const messageFields = fieldNumbers.get(messageName);
|
|
68
|
+
if (!messageFields) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
const result = {};
|
|
72
|
+
for (const [fieldName, fieldNumber] of messageFields.entries()) {
|
|
73
|
+
result[fieldName] = fieldNumber;
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
},
|
|
77
|
+
reconcileFieldOrder(messageName, fieldNames) {
|
|
78
|
+
if (!lockManager) {
|
|
79
|
+
// No lock manager, return fields in original order
|
|
80
|
+
return fieldNames;
|
|
81
|
+
}
|
|
82
|
+
// Use lock manager to reconcile field order
|
|
83
|
+
const orderedFields = lockManager.reconcileMessageFieldOrder(messageName, fieldNames);
|
|
84
|
+
// Update our internal tracking with the reconciled numbers
|
|
85
|
+
const lockData = lockManager.getLockData();
|
|
86
|
+
const messageData = lockData.messages[messageName];
|
|
87
|
+
if (messageData) {
|
|
88
|
+
// Initialize message map if needed
|
|
89
|
+
if (!fieldNumbers.has(messageName)) {
|
|
90
|
+
fieldNumbers.set(messageName, new Map());
|
|
91
|
+
}
|
|
92
|
+
const messageFields = fieldNumbers.get(messageName);
|
|
93
|
+
// Update field numbers from lock data
|
|
94
|
+
for (const fieldName of orderedFields) {
|
|
95
|
+
const fieldNumber = messageData.fields[fieldName];
|
|
96
|
+
if (fieldNumber !== undefined) {
|
|
97
|
+
messageFields.set(fieldName, fieldNumber);
|
|
98
|
+
// Update next field number
|
|
99
|
+
const currentNext = nextFieldNumbers.get(messageName) || 1;
|
|
100
|
+
if (fieldNumber >= currentNext) {
|
|
101
|
+
nextFieldNumbers.set(messageName, fieldNumber + 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return orderedFields;
|
|
107
|
+
},
|
|
108
|
+
getLockManager() {
|
|
109
|
+
return lockManager;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Assigns field numbers to a message from lock data
|
|
115
|
+
* @param messageName - The name of the message
|
|
116
|
+
* @param fieldNames - The field names to assign numbers to
|
|
117
|
+
* @param fieldNumberManager - The field number manager to use
|
|
118
|
+
*/
|
|
119
|
+
export function assignFieldNumbersFromLockData(messageName, fieldNames, fieldNumberManager) {
|
|
120
|
+
var _a;
|
|
121
|
+
const lockData = (_a = fieldNumberManager === null || fieldNumberManager === void 0 ? void 0 : fieldNumberManager.getLockManager()) === null || _a === void 0 ? void 0 : _a.getLockData();
|
|
122
|
+
if (!lockData || !fieldNumberManager)
|
|
123
|
+
return;
|
|
124
|
+
const messageData = lockData.messages[messageName];
|
|
125
|
+
if (!messageData)
|
|
126
|
+
return;
|
|
127
|
+
for (const protoFieldName of fieldNames) {
|
|
128
|
+
const fieldNumber = messageData.fields[protoFieldName];
|
|
129
|
+
if (!fieldNumber)
|
|
130
|
+
continue;
|
|
131
|
+
fieldNumberManager.assignFieldNumber(messageName, protoFieldName, fieldNumber);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=field-numbering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-numbering.js","sourceRoot":"","sources":["../../../src/operations/field-numbering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GraphQLType, GraphQLList, GraphQLNonNull } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Unwraps a GraphQL type from a GraphQLNonNull wrapper
|
|
4
|
+
*
|
|
5
|
+
* @param graphqlType - The GraphQL type to unwrap
|
|
6
|
+
* @returns The unwrapped type
|
|
7
|
+
*/
|
|
8
|
+
export declare function unwrapNonNullType<T extends GraphQLType>(graphqlType: T | GraphQLNonNull<T>): T;
|
|
9
|
+
/**
|
|
10
|
+
* Checks if a GraphQL list type contains nested lists
|
|
11
|
+
* Type guard that narrows the input type when nested lists are detected
|
|
12
|
+
*
|
|
13
|
+
* @param listType - The GraphQL list type to check
|
|
14
|
+
* @returns True if the list contains nested lists
|
|
15
|
+
*/
|
|
16
|
+
export declare function isNestedListType(listType: GraphQLList<GraphQLType>): listType is GraphQLList<GraphQLList<GraphQLType> | GraphQLNonNull<GraphQLList<GraphQLType>>>;
|
|
17
|
+
/**
|
|
18
|
+
* Calculates the nesting level of a GraphQL list type
|
|
19
|
+
*
|
|
20
|
+
* Examples:
|
|
21
|
+
* - [String] → 1
|
|
22
|
+
* - [[String]] → 2
|
|
23
|
+
* - [[[String]]] → 3
|
|
24
|
+
*
|
|
25
|
+
* @param listType - The GraphQL list type to analyze
|
|
26
|
+
* @returns The nesting level (1 for simple list, 2+ for nested lists)
|
|
27
|
+
*/
|
|
28
|
+
export declare function calculateNestingLevel(listType: GraphQLList<GraphQLType>): number;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isListType, isNonNullType } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Unwraps a GraphQL type from a GraphQLNonNull wrapper
|
|
4
|
+
*
|
|
5
|
+
* @param graphqlType - The GraphQL type to unwrap
|
|
6
|
+
* @returns The unwrapped type
|
|
7
|
+
*/
|
|
8
|
+
export function unwrapNonNullType(graphqlType) {
|
|
9
|
+
return isNonNullType(graphqlType) ? graphqlType.ofType : graphqlType;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a GraphQL list type contains nested lists
|
|
13
|
+
* Type guard that narrows the input type when nested lists are detected
|
|
14
|
+
*
|
|
15
|
+
* @param listType - The GraphQL list type to check
|
|
16
|
+
* @returns True if the list contains nested lists
|
|
17
|
+
*/
|
|
18
|
+
export function isNestedListType(listType) {
|
|
19
|
+
return isListType(listType.ofType) || (isNonNullType(listType.ofType) && isListType(listType.ofType.ofType));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Calculates the nesting level of a GraphQL list type
|
|
23
|
+
*
|
|
24
|
+
* Examples:
|
|
25
|
+
* - [String] → 1
|
|
26
|
+
* - [[String]] → 2
|
|
27
|
+
* - [[[String]]] → 3
|
|
28
|
+
*
|
|
29
|
+
* @param listType - The GraphQL list type to analyze
|
|
30
|
+
* @returns The nesting level (1 for simple list, 2+ for nested lists)
|
|
31
|
+
*/
|
|
32
|
+
export function calculateNestingLevel(listType) {
|
|
33
|
+
let level = 1;
|
|
34
|
+
let currentType = listType.ofType;
|
|
35
|
+
while (true) {
|
|
36
|
+
if (isNonNullType(currentType)) {
|
|
37
|
+
currentType = currentType.ofType;
|
|
38
|
+
}
|
|
39
|
+
else if (isListType(currentType)) {
|
|
40
|
+
currentType = currentType.ofType;
|
|
41
|
+
level++;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return level;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=list-type-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-type-utils.js","sourceRoot":"","sources":["../../../src/operations/list-type-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4C,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAwB,WAAkC;IACzF,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAE,WAAW,CAAC,MAAY,CAAC,CAAC,CAAC,WAAW,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAkC;IAElC,OAAO,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAkC;IACtE,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,WAAW,GAAgB,QAAQ,CAAC,MAAM,CAAC;IAE/C,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC;QACnC,CAAC;aAAM,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACnC,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,CAAC;YACN,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import protobuf from 'protobufjs';
|
|
2
|
+
import { SelectionSetNode, GraphQLObjectType, GraphQLType, GraphQLSchema, TypeInfo, FragmentDefinitionNode, GraphQLOutputType, GraphQLInterfaceType, GraphQLUnionType } from 'graphql';
|
|
3
|
+
import { FieldNumberManager } from './field-numbering.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for building proto messages
|
|
6
|
+
*/
|
|
7
|
+
export interface MessageBuilderOptions {
|
|
8
|
+
/** Whether to include comments/descriptions */
|
|
9
|
+
includeComments?: boolean;
|
|
10
|
+
/** Root object for adding nested types */
|
|
11
|
+
root?: protobuf.Root;
|
|
12
|
+
/** Field number manager for consistent numbering */
|
|
13
|
+
fieldNumberManager?: FieldNumberManager;
|
|
14
|
+
/** Map of fragment definitions for resolving fragment spreads */
|
|
15
|
+
fragments?: Map<string, FragmentDefinitionNode>;
|
|
16
|
+
/** Schema for type lookups */
|
|
17
|
+
schema?: GraphQLSchema;
|
|
18
|
+
/** Set to track created enums (to avoid duplicates) */
|
|
19
|
+
createdEnums?: Set<string>;
|
|
20
|
+
/** Custom scalar type mappings (scalar name -> proto type) */
|
|
21
|
+
customScalarMappings?: Record<string, string>;
|
|
22
|
+
/** Maximum recursion depth to prevent stack overflow (default: 50) */
|
|
23
|
+
maxDepth?: number;
|
|
24
|
+
/** Internal: Current recursion depth */
|
|
25
|
+
_depth?: number;
|
|
26
|
+
/** Callback to ensure nested list wrapper messages are created */
|
|
27
|
+
ensureNestedListWrapper?: (graphqlType: GraphQLOutputType) => string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Builds a Protocol Buffer message type from a GraphQL selection set
|
|
31
|
+
*
|
|
32
|
+
* @param messageName - The name for the proto message
|
|
33
|
+
* @param selectionSet - The GraphQL selection set to convert
|
|
34
|
+
* @param parentType - The GraphQL type that contains these selections
|
|
35
|
+
* @param typeInfo - TypeInfo for resolving field types
|
|
36
|
+
* @param options - Optional configuration
|
|
37
|
+
* @returns A protobuf Type object
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildMessageFromSelectionSet(messageName: string, selectionSet: SelectionSetNode, parentType: GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType, typeInfo: TypeInfo, options?: MessageBuilderOptions): protobuf.Type;
|
|
40
|
+
/**
|
|
41
|
+
* Builds a field definition for a proto message
|
|
42
|
+
*
|
|
43
|
+
* @param fieldName - The name of the field
|
|
44
|
+
* @param fieldType - The GraphQL type of the field
|
|
45
|
+
* @param fieldNumber - The proto field number
|
|
46
|
+
* @param options - Optional configuration
|
|
47
|
+
* @returns A protobuf Field object
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildFieldDefinition(fieldName: string, fieldType: GraphQLType, fieldNumber: number, options?: MessageBuilderOptions): protobuf.Field;
|
|
50
|
+
/**
|
|
51
|
+
* Builds a nested message type
|
|
52
|
+
*
|
|
53
|
+
* @param messageName - The name for the nested message
|
|
54
|
+
* @param fields - Map of field names to their GraphQL types
|
|
55
|
+
* @param options - Optional configuration
|
|
56
|
+
* @returns A protobuf Type object
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildNestedMessage(messageName: string, fields: Map<string, GraphQLType>, options?: MessageBuilderOptions): protobuf.Type;
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import protobuf from 'protobufjs';
|
|
2
|
+
import { isObjectType, isEnumType, getNamedType, isInterfaceType, isUnionType, } from 'graphql';
|
|
3
|
+
import { mapGraphQLTypeToProto } from './type-mapper.js';
|
|
4
|
+
import { assignFieldNumbersFromLockData } from './field-numbering.js';
|
|
5
|
+
import { graphqlFieldToProtoField } from '../naming-conventions.js';
|
|
6
|
+
import { buildEnumType } from './request-builder.js';
|
|
7
|
+
import { upperFirst, camelCase } from 'lodash-es';
|
|
8
|
+
/**
|
|
9
|
+
* Default maximum recursion depth to prevent stack overflow
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_MAX_DEPTH = 50;
|
|
12
|
+
/**
|
|
13
|
+
* Default starting depth for recursion tracking
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_STARTING_DEPTH = 0;
|
|
16
|
+
/**
|
|
17
|
+
* Builds a Protocol Buffer message type from a GraphQL selection set
|
|
18
|
+
*
|
|
19
|
+
* @param messageName - The name for the proto message
|
|
20
|
+
* @param selectionSet - The GraphQL selection set to convert
|
|
21
|
+
* @param parentType - The GraphQL type that contains these selections
|
|
22
|
+
* @param typeInfo - TypeInfo for resolving field types
|
|
23
|
+
* @param options - Optional configuration
|
|
24
|
+
* @returns A protobuf Type object
|
|
25
|
+
*/
|
|
26
|
+
export function buildMessageFromSelectionSet(messageName, selectionSet, parentType, typeInfo, options) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const message = new protobuf.Type(messageName);
|
|
29
|
+
const fieldNumberManager = options === null || options === void 0 ? void 0 : options.fieldNumberManager;
|
|
30
|
+
// First pass: collect all field names that will be in this message
|
|
31
|
+
const fieldNames = [];
|
|
32
|
+
const fieldSelections = new Map();
|
|
33
|
+
// Maximum recursion depth to prevent stack overflow
|
|
34
|
+
const maxDepth = (_a = options === null || options === void 0 ? void 0 : options.maxDepth) !== null && _a !== void 0 ? _a : DEFAULT_MAX_DEPTH;
|
|
35
|
+
const currentDepth = (_b = options === null || options === void 0 ? void 0 : options._depth) !== null && _b !== void 0 ? _b : DEFAULT_STARTING_DEPTH;
|
|
36
|
+
// Check depth limit at the start of building each message
|
|
37
|
+
if (currentDepth > maxDepth) {
|
|
38
|
+
throw new Error(`Maximum recursion depth (${maxDepth}) exceeded while processing selection set. ` +
|
|
39
|
+
`This may indicate deeply nested selections or circular fragment references. ` +
|
|
40
|
+
`You can increase the limit using the maxDepth option.`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Recursively collects fields from selections with protection against excessive recursion depth.
|
|
44
|
+
*
|
|
45
|
+
* Note: Circular fragment references are invalid GraphQL per the spec's NoFragmentCyclesRule.
|
|
46
|
+
* GraphQL validation should catch these before reaching proto compilation.
|
|
47
|
+
*/
|
|
48
|
+
const collectFields = (selections, currentType, depth) => {
|
|
49
|
+
// Stop condition: Check depth limit
|
|
50
|
+
if (depth > maxDepth) {
|
|
51
|
+
throw new Error(`Maximum recursion depth (${maxDepth}) exceeded while processing selection set. ` +
|
|
52
|
+
`This may indicate deeply nested selections or circular fragment references. ` +
|
|
53
|
+
`You can increase the limit using the maxDepth option.`);
|
|
54
|
+
}
|
|
55
|
+
for (const selection of selections) {
|
|
56
|
+
switch (selection.kind) {
|
|
57
|
+
case 'Field':
|
|
58
|
+
// Only object and interface types have fields that can be selected
|
|
59
|
+
// Union types require inline fragments to access their constituent types
|
|
60
|
+
if (isObjectType(currentType) || isInterfaceType(currentType)) {
|
|
61
|
+
const fieldName = selection.name.value;
|
|
62
|
+
const protoFieldName = graphqlFieldToProtoField(fieldName);
|
|
63
|
+
if (!fieldNames.includes(protoFieldName)) {
|
|
64
|
+
fieldNames.push(protoFieldName);
|
|
65
|
+
fieldSelections.set(protoFieldName, { selection, type: currentType });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
case 'InlineFragment':
|
|
70
|
+
if (selection.typeCondition && (options === null || options === void 0 ? void 0 : options.schema)) {
|
|
71
|
+
const typeName = selection.typeCondition.name.value;
|
|
72
|
+
const type = options.schema.getType(typeName);
|
|
73
|
+
if (type && (isObjectType(type) || isInterfaceType(type))) {
|
|
74
|
+
collectFields(selection.selectionSet.selections, type, depth + 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (isObjectType(currentType) || isInterfaceType(currentType)) {
|
|
78
|
+
// No type condition, but parent type supports fields
|
|
79
|
+
collectFields(selection.selectionSet.selections, currentType, depth + 1);
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case 'FragmentSpread':
|
|
83
|
+
if (options === null || options === void 0 ? void 0 : options.fragments) {
|
|
84
|
+
const fragmentDef = options.fragments.get(selection.name.value);
|
|
85
|
+
if (fragmentDef && (options === null || options === void 0 ? void 0 : options.schema)) {
|
|
86
|
+
const typeName = fragmentDef.typeCondition.name.value;
|
|
87
|
+
const type = options.schema.getType(typeName);
|
|
88
|
+
if (type && (isObjectType(type) || isInterfaceType(type) || isUnionType(type))) {
|
|
89
|
+
collectFields(fragmentDef.selectionSet.selections, type, depth + 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
// Collect fields from the selection set
|
|
98
|
+
// For union types, only inline fragments will contribute fields (handled in collectFields)
|
|
99
|
+
collectFields(selectionSet.selections, parentType, currentDepth);
|
|
100
|
+
// Reconcile field order using lock manager if available
|
|
101
|
+
let orderedFieldNames = fieldNames;
|
|
102
|
+
if (fieldNumberManager && 'reconcileFieldOrder' in fieldNumberManager) {
|
|
103
|
+
orderedFieldNames = fieldNumberManager.reconcileFieldOrder(messageName, fieldNames);
|
|
104
|
+
}
|
|
105
|
+
// Second pass: process fields in reconciled order
|
|
106
|
+
// Pre-assign field numbers from lock data if available
|
|
107
|
+
assignFieldNumbersFromLockData(messageName, orderedFieldNames, fieldNumberManager);
|
|
108
|
+
for (const protoFieldName of orderedFieldNames) {
|
|
109
|
+
const fieldData = fieldSelections.get(protoFieldName);
|
|
110
|
+
if (fieldData) {
|
|
111
|
+
const fieldOptions = {
|
|
112
|
+
...options,
|
|
113
|
+
_depth: currentDepth,
|
|
114
|
+
};
|
|
115
|
+
processFieldSelection(fieldData.selection, message, fieldData.type, typeInfo, fieldOptions, fieldNumberManager);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return message;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets or assigns a field number for a proto field
|
|
122
|
+
*/
|
|
123
|
+
function getOrAssignFieldNumber(message, protoFieldName, fieldNumberManager) {
|
|
124
|
+
const existingFieldNumber = fieldNumberManager === null || fieldNumberManager === void 0 ? void 0 : fieldNumberManager.getFieldNumber(message.name, protoFieldName);
|
|
125
|
+
if (existingFieldNumber !== undefined) {
|
|
126
|
+
return existingFieldNumber;
|
|
127
|
+
}
|
|
128
|
+
if (fieldNumberManager) {
|
|
129
|
+
const fieldNumber = fieldNumberManager.getNextFieldNumber(message.name);
|
|
130
|
+
fieldNumberManager.assignFieldNumber(message.name, protoFieldName, fieldNumber);
|
|
131
|
+
return fieldNumber;
|
|
132
|
+
}
|
|
133
|
+
return message.fieldsArray.length + 1;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resolves the final type name and repetition flag, handling nested list wrappers
|
|
137
|
+
*/
|
|
138
|
+
function resolveTypeNameAndRepetition(baseTypeName, protoTypeInfo, fieldType, options) {
|
|
139
|
+
let typeName = baseTypeName;
|
|
140
|
+
let isRepeated = protoTypeInfo.isRepeated;
|
|
141
|
+
if (protoTypeInfo.requiresNestedWrapper && (options === null || options === void 0 ? void 0 : options.ensureNestedListWrapper)) {
|
|
142
|
+
typeName = options.ensureNestedListWrapper(fieldType);
|
|
143
|
+
isRepeated = false; // Wrapper handles the repetition
|
|
144
|
+
}
|
|
145
|
+
return { typeName, isRepeated };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Creates and configures a proto field with comments
|
|
149
|
+
*/
|
|
150
|
+
function createProtoField(protoFieldName, fieldNumber, typeName, isRepeated, fieldDef, options) {
|
|
151
|
+
const protoField = new protobuf.Field(protoFieldName, fieldNumber, typeName);
|
|
152
|
+
if (isRepeated) {
|
|
153
|
+
protoField.repeated = true;
|
|
154
|
+
}
|
|
155
|
+
if ((options === null || options === void 0 ? void 0 : options.includeComments) && fieldDef.description) {
|
|
156
|
+
protoField.comment = fieldDef.description;
|
|
157
|
+
}
|
|
158
|
+
return protoField;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Ensures an enum type is created and added to the root
|
|
162
|
+
*/
|
|
163
|
+
function ensureEnumCreated(namedType, options) {
|
|
164
|
+
if (!options.root) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const enumTypeName = namedType.name;
|
|
168
|
+
// Initialize createdEnums in options if missing to ensure persistence across calls
|
|
169
|
+
if (!options.createdEnums) {
|
|
170
|
+
options.createdEnums = new Set();
|
|
171
|
+
}
|
|
172
|
+
if (!options.createdEnums.has(enumTypeName)) {
|
|
173
|
+
const protoEnum = buildEnumType(namedType, {
|
|
174
|
+
includeComments: options.includeComments,
|
|
175
|
+
});
|
|
176
|
+
options.root.add(protoEnum);
|
|
177
|
+
options.createdEnums.add(enumTypeName);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Processes a field selection and adds it to the message
|
|
182
|
+
*/
|
|
183
|
+
function processFieldSelection(field, message, parentType, typeInfo, options, fieldNumberManager) {
|
|
184
|
+
var _a;
|
|
185
|
+
const fieldName = field.name.value;
|
|
186
|
+
// Skip __typename - it's a GraphQL introspection field that doesn't need to be in proto
|
|
187
|
+
if (fieldName === '__typename') {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const protoFieldName = graphqlFieldToProtoField(fieldName);
|
|
191
|
+
// Check if field already exists in the message (avoid duplicates)
|
|
192
|
+
if (message.fields[protoFieldName]) {
|
|
193
|
+
return; // Field already added, skip
|
|
194
|
+
}
|
|
195
|
+
// Get the field definition from the parent type
|
|
196
|
+
// Union types don't have fields directly, so skip field validation for them
|
|
197
|
+
if (isUnionType(parentType)) {
|
|
198
|
+
// Union types should only be processed through inline fragments
|
|
199
|
+
// This shouldn't happen in normal GraphQL, but we'll handle it gracefully
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const fieldDef = parentType.getFields()[fieldName];
|
|
203
|
+
if (!fieldDef) {
|
|
204
|
+
throw new Error(`Field "${fieldName}" does not exist on type "${parentType.name}". ` +
|
|
205
|
+
`GraphQL validation should be performed before proto compilation.`);
|
|
206
|
+
}
|
|
207
|
+
const fieldType = fieldDef.type;
|
|
208
|
+
// Determine the base type name based on whether we have a selection set
|
|
209
|
+
let baseTypeName;
|
|
210
|
+
if (field.selectionSet) {
|
|
211
|
+
// Build nested message for object types
|
|
212
|
+
const namedType = getNamedType(fieldType);
|
|
213
|
+
if (isObjectType(namedType) || isInterfaceType(namedType) || isUnionType(namedType)) {
|
|
214
|
+
const nestedMessageName = upperFirst(camelCase(fieldName));
|
|
215
|
+
const nestedOptions = {
|
|
216
|
+
...options,
|
|
217
|
+
_depth: ((_a = options === null || options === void 0 ? void 0 : options._depth) !== null && _a !== void 0 ? _a : 0) + 1,
|
|
218
|
+
};
|
|
219
|
+
const nestedMessage = buildMessageFromSelectionSet(nestedMessageName, field.selectionSet, namedType, typeInfo, nestedOptions);
|
|
220
|
+
message.add(nestedMessage);
|
|
221
|
+
baseTypeName = nestedMessageName;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
return; // Shouldn't happen with valid GraphQL
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// Handle scalar/enum fields
|
|
229
|
+
const namedType = getNamedType(fieldType);
|
|
230
|
+
if (isEnumType(namedType) && (options === null || options === void 0 ? void 0 : options.root)) {
|
|
231
|
+
ensureEnumCreated(namedType, options);
|
|
232
|
+
}
|
|
233
|
+
const protoTypeInfo = mapGraphQLTypeToProto(fieldType, {
|
|
234
|
+
customScalarMappings: options === null || options === void 0 ? void 0 : options.customScalarMappings,
|
|
235
|
+
});
|
|
236
|
+
baseTypeName = protoTypeInfo.typeName;
|
|
237
|
+
}
|
|
238
|
+
// Common logic for both branches
|
|
239
|
+
const protoTypeInfo = mapGraphQLTypeToProto(fieldType, {
|
|
240
|
+
customScalarMappings: options === null || options === void 0 ? void 0 : options.customScalarMappings,
|
|
241
|
+
});
|
|
242
|
+
const { typeName, isRepeated } = resolveTypeNameAndRepetition(baseTypeName, protoTypeInfo, fieldType, options);
|
|
243
|
+
const fieldNumber = getOrAssignFieldNumber(message, protoFieldName, fieldNumberManager);
|
|
244
|
+
const protoField = createProtoField(protoFieldName, fieldNumber, typeName, isRepeated, fieldDef, options);
|
|
245
|
+
message.add(protoField);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Processes an inline fragment and adds its selections to the message
|
|
249
|
+
* Inline fragments allow type-specific field selections on interfaces/unions
|
|
250
|
+
*/
|
|
251
|
+
function processInlineFragment(fragment, message, parentType, typeInfo, options, fieldNumberManager) {
|
|
252
|
+
// Determine the type for this inline fragment
|
|
253
|
+
let fragmentType;
|
|
254
|
+
if (fragment.typeCondition) {
|
|
255
|
+
// Type condition specified: ... on User
|
|
256
|
+
const typeName = fragment.typeCondition.name.value;
|
|
257
|
+
const schema = options === null || options === void 0 ? void 0 : options.schema;
|
|
258
|
+
if (!schema) {
|
|
259
|
+
// Without schema, we can't resolve the type - skip
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const type = schema.getType(typeName);
|
|
263
|
+
if (!type || !(isObjectType(type) || isInterfaceType(type) || isUnionType(type))) {
|
|
264
|
+
// Type not found or not a supported type - skip
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
fragmentType = type;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// No type condition: just process with parent type
|
|
271
|
+
fragmentType = parentType;
|
|
272
|
+
}
|
|
273
|
+
// Process all selections in the inline fragment with the resolved type
|
|
274
|
+
if (fragment.selectionSet) {
|
|
275
|
+
for (const selection of fragment.selectionSet.selections) {
|
|
276
|
+
if (selection.kind === 'Field') {
|
|
277
|
+
processFieldSelection(selection, message, fragmentType, typeInfo, options, fieldNumberManager);
|
|
278
|
+
}
|
|
279
|
+
else if (selection.kind === 'InlineFragment') {
|
|
280
|
+
// Nested inline fragment
|
|
281
|
+
processInlineFragment(selection, message, fragmentType, typeInfo, options, fieldNumberManager);
|
|
282
|
+
}
|
|
283
|
+
else if (selection.kind === 'FragmentSpread') {
|
|
284
|
+
processFragmentSpread(selection, message, fragmentType, typeInfo, options, fieldNumberManager);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Processes a fragment spread and adds its selections to the message
|
|
291
|
+
* Fragment spreads reference named fragment definitions
|
|
292
|
+
*/
|
|
293
|
+
function processFragmentSpread(spread, message, parentType, typeInfo, options, fieldNumberManager) {
|
|
294
|
+
const fragmentName = spread.name.value;
|
|
295
|
+
const fragments = options === null || options === void 0 ? void 0 : options.fragments;
|
|
296
|
+
if (!fragments) {
|
|
297
|
+
// No fragments provided - skip
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const fragmentDef = fragments.get(fragmentName);
|
|
301
|
+
if (!fragmentDef) {
|
|
302
|
+
// Fragment definition not found - skip
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// Resolve the fragment's type condition
|
|
306
|
+
const typeName = fragmentDef.typeCondition.name.value;
|
|
307
|
+
const schema = options === null || options === void 0 ? void 0 : options.schema;
|
|
308
|
+
if (!schema) {
|
|
309
|
+
// Without schema, we can't resolve the type - skip
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const type = schema.getType(typeName);
|
|
313
|
+
if (!type || !(isObjectType(type) || isInterfaceType(type) || isUnionType(type))) {
|
|
314
|
+
// Type not found or not a supported type - skip
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
// Process the fragment's selection set with the resolved type
|
|
318
|
+
for (const selection of fragmentDef.selectionSet.selections) {
|
|
319
|
+
if (selection.kind === 'Field') {
|
|
320
|
+
processFieldSelection(selection, message, type, typeInfo, options, fieldNumberManager);
|
|
321
|
+
}
|
|
322
|
+
else if (selection.kind === 'InlineFragment') {
|
|
323
|
+
processInlineFragment(selection, message, type, typeInfo, options, fieldNumberManager);
|
|
324
|
+
}
|
|
325
|
+
else if (selection.kind === 'FragmentSpread') {
|
|
326
|
+
// Nested fragment spread (fragment inside fragment)
|
|
327
|
+
processFragmentSpread(selection, message, type, typeInfo, options, fieldNumberManager);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Builds a field definition for a proto message
|
|
333
|
+
*
|
|
334
|
+
* @param fieldName - The name of the field
|
|
335
|
+
* @param fieldType - The GraphQL type of the field
|
|
336
|
+
* @param fieldNumber - The proto field number
|
|
337
|
+
* @param options - Optional configuration
|
|
338
|
+
* @returns A protobuf Field object
|
|
339
|
+
*/
|
|
340
|
+
export function buildFieldDefinition(fieldName, fieldType, fieldNumber, options) {
|
|
341
|
+
const protoFieldName = graphqlFieldToProtoField(fieldName);
|
|
342
|
+
const typeInfo = mapGraphQLTypeToProto(fieldType, {
|
|
343
|
+
customScalarMappings: options === null || options === void 0 ? void 0 : options.customScalarMappings,
|
|
344
|
+
});
|
|
345
|
+
const field = new protobuf.Field(protoFieldName, fieldNumber, typeInfo.typeName);
|
|
346
|
+
if (typeInfo.isRepeated) {
|
|
347
|
+
field.repeated = true;
|
|
348
|
+
}
|
|
349
|
+
return field;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Builds a nested message type
|
|
353
|
+
*
|
|
354
|
+
* @param messageName - The name for the nested message
|
|
355
|
+
* @param fields - Map of field names to their GraphQL types
|
|
356
|
+
* @param options - Optional configuration
|
|
357
|
+
* @returns A protobuf Type object
|
|
358
|
+
*/
|
|
359
|
+
export function buildNestedMessage(messageName, fields, options) {
|
|
360
|
+
const message = new protobuf.Type(messageName);
|
|
361
|
+
const fieldNumberManager = options === null || options === void 0 ? void 0 : options.fieldNumberManager;
|
|
362
|
+
let fieldNumber = 1;
|
|
363
|
+
for (const [fieldName, fieldType] of fields.entries()) {
|
|
364
|
+
const protoFieldName = graphqlFieldToProtoField(fieldName);
|
|
365
|
+
if (fieldNumberManager) {
|
|
366
|
+
fieldNumber = fieldNumberManager.getNextFieldNumber(messageName);
|
|
367
|
+
fieldNumberManager.assignFieldNumber(messageName, protoFieldName, fieldNumber);
|
|
368
|
+
}
|
|
369
|
+
const field = buildFieldDefinition(fieldName, fieldType, fieldNumber, options);
|
|
370
|
+
message.add(field);
|
|
371
|
+
if (!fieldNumberManager) {
|
|
372
|
+
fieldNumber++;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return message;
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=message-builder.js.map
|