@wundergraph/protographic 0.19.0 → 0.19.2
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/dist/src/abstract-selection-rewriter.js +1 -1
- package/dist/src/abstract-selection-rewriter.js.map +1 -1
- package/dist/src/required-fields-visitor.js +1 -1
- package/dist/src/required-fields-visitor.js.map +1 -1
- package/dist/src/sdl-validation-visitor.d.ts +2 -62
- package/dist/src/sdl-validation-visitor.js +53 -43
- package/dist/src/sdl-validation-visitor.js.map +1 -1
- package/dist/src/selection-set-validation-visitor.d.ts +70 -0
- package/dist/src/selection-set-validation-visitor.js +148 -0
- package/dist/src/selection-set-validation-visitor.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Kind, visit, } from 'graphql';
|
|
2
|
+
import { AbstractSelectionRewriter } from './abstract-selection-rewriter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validates selection sets within @requires directive field sets.
|
|
5
|
+
*
|
|
6
|
+
* This visitor traverses a parsed field set document and ensures that inline
|
|
7
|
+
* fragments on composite types (interfaces, unions) include `__typename` for
|
|
8
|
+
* type discrimination in protobuf. The `__typename` field can appear either
|
|
9
|
+
* in the parent field's selection set or within each inline fragment's
|
|
10
|
+
* selection set — at least one of these locations must contain it.
|
|
11
|
+
*
|
|
12
|
+
* Before validation, the selection set is normalized by the
|
|
13
|
+
* {@link AbstractSelectionRewriter}, which distributes parent-level fields
|
|
14
|
+
* (including `__typename`) into each inline fragment.
|
|
15
|
+
*/
|
|
16
|
+
export class SelectionSetValidationVisitor {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new SelectionSetValidationVisitor.
|
|
19
|
+
*
|
|
20
|
+
* @param operationDocument - The parsed GraphQL document representing the field set
|
|
21
|
+
* @param objectType - The root GraphQL object type to validate against
|
|
22
|
+
* @param schema - The full GraphQL schema, used for normalization of abstract type selections
|
|
23
|
+
*/
|
|
24
|
+
constructor(operationDocument, objectType, schema) {
|
|
25
|
+
this.fieldSelectionSetStack = [];
|
|
26
|
+
this.validationResult = {
|
|
27
|
+
errors: [],
|
|
28
|
+
warnings: [],
|
|
29
|
+
};
|
|
30
|
+
this.operationDocument = operationDocument;
|
|
31
|
+
this.objectType = objectType;
|
|
32
|
+
this.schema = schema;
|
|
33
|
+
this.normalizeSelectionSet();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Executes the validation by traversing the operation document.
|
|
37
|
+
* After calling this method, use `getValidationResult()` to retrieve any errors or warnings.
|
|
38
|
+
*/
|
|
39
|
+
visit() {
|
|
40
|
+
visit(this.operationDocument, this.createASTVisitor());
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the validation result containing any errors and warnings found during traversal.
|
|
44
|
+
*
|
|
45
|
+
* @returns The validation result with errors and warnings arrays
|
|
46
|
+
*/
|
|
47
|
+
getValidationResult() {
|
|
48
|
+
return this.validationResult;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalizes the parsed field set operation by rewriting abstract selections.
|
|
52
|
+
* This ensures consistent handling of interface and union type selections.
|
|
53
|
+
*/
|
|
54
|
+
normalizeSelectionSet() {
|
|
55
|
+
const visitor = new AbstractSelectionRewriter(this.operationDocument, this.schema, this.objectType);
|
|
56
|
+
visitor.normalize();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates the AST visitor configuration for traversing the document.
|
|
60
|
+
*
|
|
61
|
+
* @returns An ASTVisitor object with enter/leave handlers for SelectionSet nodes
|
|
62
|
+
*/
|
|
63
|
+
createASTVisitor() {
|
|
64
|
+
return {
|
|
65
|
+
SelectionSet: {
|
|
66
|
+
enter: (node, key, parent, path, ancestors) => {
|
|
67
|
+
return this.onEnterSelectionSet({ node, key, parent, path, ancestors });
|
|
68
|
+
},
|
|
69
|
+
leave: (node, key, parent, path, ancestors) => {
|
|
70
|
+
this.onLeaveSelectionSet({ node, key, parent, path, ancestors });
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handles entering a selection set node during traversal.
|
|
77
|
+
*
|
|
78
|
+
* @param ctx - The visit context containing the selection set node and its parent
|
|
79
|
+
*/
|
|
80
|
+
onEnterSelectionSet(ctx) {
|
|
81
|
+
var _a;
|
|
82
|
+
// When we have no parent, we are at the root of the selection set.
|
|
83
|
+
if (!ctx.parent) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// We store the ancestor field selection sets on the stack so we can restore them when leaving a nested field.
|
|
87
|
+
if (this.isFieldNode(ctx.parent)) {
|
|
88
|
+
if (this.currentFieldSelectionSet) {
|
|
89
|
+
this.fieldSelectionSetStack.push(this.currentFieldSelectionSet);
|
|
90
|
+
}
|
|
91
|
+
this.currentFieldSelectionSet = ctx.node;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// We currently only check for inline fragments.
|
|
95
|
+
if (!this.isInlineFragment(ctx.parent)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// either the selection set of the inline fragment or the parent selection set must contain __typename.
|
|
99
|
+
if (!this.selectionSetContainsTypename(ctx.node) &&
|
|
100
|
+
!this.selectionSetContainsTypename(this.currentFieldSelectionSet)) {
|
|
101
|
+
const fieldPath = this.getFieldPath(ctx.ancestors);
|
|
102
|
+
const pathSuffix = fieldPath ? ` in "${fieldPath}"` : '';
|
|
103
|
+
this.validationResult.errors.push(`Selection set must contain __typename for inline fragment ${(_a = ctx.parent.typeCondition) === null || _a === void 0 ? void 0 : _a.name.value}${pathSuffix}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
selectionSetContainsTypename(selectionSet) {
|
|
107
|
+
if (!selectionSet) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return selectionSet.selections.some((selection) => selection.kind === Kind.FIELD && selection.name.value === '__typename');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Handles leaving a selection set node during traversal.
|
|
114
|
+
* Restores the previous field selection set from the stack when leaving a field's selection set.
|
|
115
|
+
*
|
|
116
|
+
* @param ctx - The visit context containing the selection set node and its parent
|
|
117
|
+
*/
|
|
118
|
+
onLeaveSelectionSet(ctx) {
|
|
119
|
+
if (!ctx.parent) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (this.isFieldNode(ctx.parent)) {
|
|
123
|
+
this.currentFieldSelectionSet = this.fieldSelectionSetStack.pop();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
getFieldPath(ancestors) {
|
|
127
|
+
if (!ancestors) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
return ancestors
|
|
131
|
+
.filter((a) => this.isFieldNode(a))
|
|
132
|
+
.map((f) => f.name.value)
|
|
133
|
+
.join('.');
|
|
134
|
+
}
|
|
135
|
+
isInlineFragment(node) {
|
|
136
|
+
if (Array.isArray(node)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return node.kind === Kind.INLINE_FRAGMENT;
|
|
140
|
+
}
|
|
141
|
+
isFieldNode(node) {
|
|
142
|
+
if (Array.isArray(node)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return node.kind === Kind.FIELD;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=selection-set-validation-visitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-set-validation-visitor.js","sourceRoot":"","sources":["../../src/selection-set-validation-visitor.ts"],"names":[],"mappings":"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"}
|