1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.visitResult = exports.visitErrors = exports.visitData = void 0;
|
4 | const graphql_1 = require("graphql");
|
5 | const collectFields_js_1 = require("./collectFields.js");
|
6 | const getOperationASTFromRequest_js_1 = require("./getOperationASTFromRequest.js");
|
7 | function visitData(data, enter, leave) {
|
8 | if (Array.isArray(data)) {
|
9 | return data.map(value => visitData(value, enter, leave));
|
10 | }
|
11 | else if (typeof data === 'object') {
|
12 | const newData = enter != null ? enter(data) : data;
|
13 | if (newData != null) {
|
14 | for (const key in newData) {
|
15 | const value = newData[key];
|
16 | Object.defineProperty(newData, key, {
|
17 | value: visitData(value, enter, leave),
|
18 | });
|
19 | }
|
20 | }
|
21 | return leave != null ? leave(newData) : newData;
|
22 | }
|
23 | return data;
|
24 | }
|
25 | exports.visitData = visitData;
|
26 | function visitErrors(errors, visitor) {
|
27 | return errors.map(error => visitor(error));
|
28 | }
|
29 | exports.visitErrors = visitErrors;
|
30 | function visitResult(result, request, schema, resultVisitorMap, errorVisitorMap) {
|
31 | const fragments = request.document.definitions.reduce((acc, def) => {
|
32 | if (def.kind === graphql_1.Kind.FRAGMENT_DEFINITION) {
|
33 | acc[def.name.value] = def;
|
34 | }
|
35 | return acc;
|
36 | }, {});
|
37 | const variableValues = request.variables || {};
|
38 | const errorInfo = {
|
39 | segmentInfoMap: new Map(),
|
40 | unpathedErrors: new Set(),
|
41 | };
|
42 | const data = result.data;
|
43 | const errors = result.errors;
|
44 | const visitingErrors = errors != null && errorVisitorMap != null;
|
45 | const operationDocumentNode = (0, getOperationASTFromRequest_js_1.getOperationASTFromRequest)(request);
|
46 | if (data != null && operationDocumentNode != null) {
|
47 | result.data = visitRoot(data, operationDocumentNode, schema, fragments, variableValues, resultVisitorMap, visitingErrors ? errors : undefined, errorInfo);
|
48 | }
|
49 | if (errors != null && errorVisitorMap) {
|
50 | result.errors = visitErrorsByType(errors, errorVisitorMap, errorInfo);
|
51 | }
|
52 | return result;
|
53 | }
|
54 | exports.visitResult = visitResult;
|
55 | function visitErrorsByType(errors, errorVisitorMap, errorInfo) {
|
56 | const segmentInfoMap = errorInfo.segmentInfoMap;
|
57 | const unpathedErrors = errorInfo.unpathedErrors;
|
58 | const unpathedErrorVisitor = errorVisitorMap['__unpathed'];
|
59 | return errors.map(originalError => {
|
60 | const pathSegmentsInfo = segmentInfoMap.get(originalError);
|
61 | const newError = pathSegmentsInfo == null
|
62 | ? originalError
|
63 | : pathSegmentsInfo.reduceRight((acc, segmentInfo) => {
|
64 | const typeName = segmentInfo.type.name;
|
65 | const typeVisitorMap = errorVisitorMap[typeName];
|
66 | if (typeVisitorMap == null) {
|
67 | return acc;
|
68 | }
|
69 | const errorVisitor = typeVisitorMap[segmentInfo.fieldName];
|
70 | return errorVisitor == null ? acc : errorVisitor(acc, segmentInfo.pathIndex);
|
71 | }, originalError);
|
72 | if (unpathedErrorVisitor && unpathedErrors.has(originalError)) {
|
73 | return unpathedErrorVisitor(newError);
|
74 | }
|
75 | return newError;
|
76 | });
|
77 | }
|
78 | function getOperationRootType(schema, operationDef) {
|
79 | switch (operationDef.operation) {
|
80 | case 'query':
|
81 | return schema.getQueryType();
|
82 | case 'mutation':
|
83 | return schema.getMutationType();
|
84 | case 'subscription':
|
85 | return schema.getSubscriptionType();
|
86 | }
|
87 | }
|
88 | function visitRoot(root, operation, schema, fragments, variableValues, resultVisitorMap, errors, errorInfo) {
|
89 | const operationRootType = getOperationRootType(schema, operation);
|
90 | const { fields: collectedFields } = (0, collectFields_js_1.collectFields)(schema, fragments, variableValues, operationRootType, operation.selectionSet);
|
91 | return visitObjectValue(root, operationRootType, collectedFields, schema, fragments, variableValues, resultVisitorMap, 0, errors, errorInfo);
|
92 | }
|
93 | function visitObjectValue(object, type, fieldNodeMap, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors, errorInfo) {
|
94 | const fieldMap = type.getFields();
|
95 | const typeVisitorMap = resultVisitorMap?.[type.name];
|
96 | const enterObject = typeVisitorMap?.__enter;
|
97 | const newObject = enterObject != null ? enterObject(object) : object;
|
98 | let sortedErrors;
|
99 | let errorMap = null;
|
100 | if (errors != null) {
|
101 | sortedErrors = sortErrorsByPathSegment(errors, pathIndex);
|
102 | errorMap = sortedErrors.errorMap;
|
103 | for (const error of sortedErrors.unpathedErrors) {
|
104 | errorInfo.unpathedErrors.add(error);
|
105 | }
|
106 | }
|
107 | for (const [responseKey, subFieldNodes] of fieldNodeMap) {
|
108 | const fieldName = subFieldNodes[0].name.value;
|
109 | let fieldType = fieldMap[fieldName]?.type;
|
110 | if (fieldType == null) {
|
111 | switch (fieldName) {
|
112 | case '__typename':
|
113 | fieldType = graphql_1.TypeNameMetaFieldDef.type;
|
114 | break;
|
115 | case '__schema':
|
116 | fieldType = graphql_1.SchemaMetaFieldDef.type;
|
117 | break;
|
118 | case '__type':
|
119 | fieldType = graphql_1.TypeMetaFieldDef.type;
|
120 | break;
|
121 | }
|
122 | }
|
123 | const newPathIndex = pathIndex + 1;
|
124 | let fieldErrors;
|
125 | if (errorMap) {
|
126 | fieldErrors = errorMap[responseKey];
|
127 | if (fieldErrors != null) {
|
128 | delete errorMap[responseKey];
|
129 | }
|
130 | addPathSegmentInfo(type, fieldName, newPathIndex, fieldErrors, errorInfo);
|
131 | }
|
132 | const newValue = visitFieldValue(object[responseKey], fieldType, subFieldNodes, schema, fragments, variableValues, resultVisitorMap, newPathIndex, fieldErrors, errorInfo);
|
133 | updateObject(newObject, responseKey, newValue, typeVisitorMap, fieldName);
|
134 | }
|
135 | const oldTypename = newObject.__typename;
|
136 | if (oldTypename != null) {
|
137 | updateObject(newObject, '__typename', oldTypename, typeVisitorMap, '__typename');
|
138 | }
|
139 | if (errorMap) {
|
140 | for (const errorsKey in errorMap) {
|
141 | const errors = errorMap[errorsKey];
|
142 | for (const error of errors) {
|
143 | errorInfo.unpathedErrors.add(error);
|
144 | }
|
145 | }
|
146 | }
|
147 | const leaveObject = typeVisitorMap?.__leave;
|
148 | return leaveObject != null ? leaveObject(newObject) : newObject;
|
149 | }
|
150 | function updateObject(object, responseKey, newValue, typeVisitorMap, fieldName) {
|
151 | if (typeVisitorMap == null) {
|
152 | object[responseKey] = newValue;
|
153 | return;
|
154 | }
|
155 | const fieldVisitor = typeVisitorMap[fieldName];
|
156 | if (fieldVisitor == null) {
|
157 | object[responseKey] = newValue;
|
158 | return;
|
159 | }
|
160 | const visitedValue = fieldVisitor(newValue);
|
161 | if (visitedValue === undefined) {
|
162 | delete object[responseKey];
|
163 | return;
|
164 | }
|
165 | object[responseKey] = visitedValue;
|
166 | }
|
167 | function visitListValue(list, returnType, fieldNodes, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors, errorInfo) {
|
168 | return list.map(listMember => visitFieldValue(listMember, returnType, fieldNodes, schema, fragments, variableValues, resultVisitorMap, pathIndex + 1, errors, errorInfo));
|
169 | }
|
170 | function visitFieldValue(value, returnType, fieldNodes, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors = [], errorInfo) {
|
171 | if (value == null) {
|
172 | return value;
|
173 | }
|
174 | const nullableType = (0, graphql_1.getNullableType)(returnType);
|
175 | if ((0, graphql_1.isListType)(nullableType)) {
|
176 | return visitListValue(value, nullableType.ofType, fieldNodes, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors, errorInfo);
|
177 | }
|
178 | else if ((0, graphql_1.isAbstractType)(nullableType)) {
|
179 | const finalType = schema.getType(value.__typename);
|
180 | let { fields: collectedFields, patches } = (0, collectFields_js_1.collectSubFields)(schema, fragments, variableValues, finalType, fieldNodes);
|
181 | if (patches.length) {
|
182 | collectedFields = new Map(collectedFields);
|
183 | for (const patch of patches) {
|
184 | for (const [responseKey, fields] of patch.fields) {
|
185 | const existingFields = collectedFields.get(responseKey);
|
186 | if (existingFields) {
|
187 | existingFields.push(...fields);
|
188 | }
|
189 | else {
|
190 | collectedFields.set(responseKey, fields);
|
191 | }
|
192 | }
|
193 | }
|
194 | }
|
195 | return visitObjectValue(value, finalType, collectedFields, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors, errorInfo);
|
196 | }
|
197 | else if ((0, graphql_1.isObjectType)(nullableType)) {
|
198 | let { fields: collectedFields, patches } = (0, collectFields_js_1.collectSubFields)(schema, fragments, variableValues, nullableType, fieldNodes);
|
199 | if (patches.length) {
|
200 | collectedFields = new Map(collectedFields);
|
201 | for (const patch of patches) {
|
202 | for (const [responseKey, fields] of patch.fields) {
|
203 | const existingFields = collectedFields.get(responseKey);
|
204 | if (existingFields) {
|
205 | existingFields.push(...fields);
|
206 | }
|
207 | else {
|
208 | collectedFields.set(responseKey, fields);
|
209 | }
|
210 | }
|
211 | }
|
212 | }
|
213 | return visitObjectValue(value, nullableType, collectedFields, schema, fragments, variableValues, resultVisitorMap, pathIndex, errors, errorInfo);
|
214 | }
|
215 | const typeVisitorMap = resultVisitorMap?.[nullableType.name];
|
216 | if (typeVisitorMap == null) {
|
217 | return value;
|
218 | }
|
219 | const visitedValue = typeVisitorMap(value);
|
220 | return visitedValue === undefined ? value : visitedValue;
|
221 | }
|
222 | function sortErrorsByPathSegment(errors, pathIndex) {
|
223 | const errorMap = Object.create(null);
|
224 | const unpathedErrors = new Set();
|
225 | for (const error of errors) {
|
226 | const pathSegment = error.path?.[pathIndex];
|
227 | if (pathSegment == null) {
|
228 | unpathedErrors.add(error);
|
229 | continue;
|
230 | }
|
231 | if (pathSegment in errorMap) {
|
232 | errorMap[pathSegment].push(error);
|
233 | }
|
234 | else {
|
235 | errorMap[pathSegment] = [error];
|
236 | }
|
237 | }
|
238 | return {
|
239 | errorMap,
|
240 | unpathedErrors,
|
241 | };
|
242 | }
|
243 | function addPathSegmentInfo(type, fieldName, pathIndex, errors = [], errorInfo) {
|
244 | for (const error of errors) {
|
245 | const segmentInfo = {
|
246 | type,
|
247 | fieldName,
|
248 | pathIndex,
|
249 | };
|
250 | const pathSegmentsInfo = errorInfo.segmentInfoMap.get(error);
|
251 | if (pathSegmentsInfo == null) {
|
252 | errorInfo.segmentInfoMap.set(error, [segmentInfo]);
|
253 | }
|
254 | else {
|
255 | pathSegmentsInfo.push(segmentInfo);
|
256 | }
|
257 | }
|
258 | }
|