UNPKG

16.6 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7const tslib = require('tslib');
8const DataLoader = _interopDefault(require('dataloader'));
9const graphql = require('graphql');
10const utils = require('@graphql-tools/utils/es5');
11
12// adapted from https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js
13function createPrefix(index) {
14 return "graphqlTools" + index + "_";
15}
16function parseKey(prefixedKey) {
17 var match = /^graphqlTools([\d]+)_(.*)$/.exec(prefixedKey);
18 if (match && match.length === 3 && !isNaN(Number(match[1])) && match[2]) {
19 return { index: Number(match[1]), originalKey: match[2] };
20 }
21 throw new Error("Key " + prefixedKey + " is not correctly prefixed");
22}
23
24// adapted from https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js
25/**
26 * Merge multiple queries into a single query in such a way that query results
27 * can be split and transformed as if they were obtained by running original queries.
28 *
29 * Merging algorithm involves several transformations:
30 * 1. Replace top-level fragment spreads with inline fragments (... on Query {})
31 * 2. Add unique aliases to all top-level query fields (including those on inline fragments)
32 * 3. Prefix all variable definitions and variable usages
33 * 4. Prefix names (and spreads) of fragments
34 *
35 * i.e transform:
36 * [
37 * `query Foo($id: ID!) { foo, bar(id: $id), ...FooQuery }
38 * fragment FooQuery on Query { baz }`,
39 *
40 * `query Bar($id: ID!) { foo: baz, bar(id: $id), ... on Query { baz } }`
41 * ]
42 * to:
43 * query (
44 * $graphqlTools1_id: ID!
45 * $graphqlTools2_id: ID!
46 * ) {
47 * graphqlTools1_foo: foo,
48 * graphqlTools1_bar: bar(id: $graphqlTools1_id)
49 * ... on Query {
50 * graphqlTools1__baz: baz
51 * }
52 * graphqlTools1__foo: baz
53 * graphqlTools1__bar: bar(id: $graphqlTools1__id)
54 * ... on Query {
55 * graphqlTools1__baz: baz
56 * }
57 * }
58 */
59function mergeRequests(requests, extensionsReducer) {
60 var e_1, _a;
61 var mergedVariables = Object.create(null);
62 var mergedVariableDefinitions = [];
63 var mergedSelections = [];
64 var mergedFragmentDefinitions = [];
65 var mergedExtensions = Object.create(null);
66 for (var index in requests) {
67 var request = requests[index];
68 var prefixedRequests = prefixRequest(createPrefix(index), request);
69 try {
70 for (var _b = (e_1 = void 0, tslib.__values(prefixedRequests.document.definitions)), _c = _b.next(); !_c.done; _c = _b.next()) {
71 var def = _c.value;
72 if (isOperationDefinition(def)) {
73 mergedSelections.push.apply(mergedSelections, tslib.__spreadArray([], tslib.__read(def.selectionSet.selections)));
74 if (def.variableDefinitions) {
75 mergedVariableDefinitions.push.apply(mergedVariableDefinitions, tslib.__spreadArray([], tslib.__read(def.variableDefinitions)));
76 }
77 }
78 if (isFragmentDefinition(def)) {
79 mergedFragmentDefinitions.push(def);
80 }
81 }
82 }
83 catch (e_1_1) { e_1 = { error: e_1_1 }; }
84 finally {
85 try {
86 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
87 }
88 finally { if (e_1) throw e_1.error; }
89 }
90 Object.assign(mergedVariables, prefixedRequests.variables);
91 mergedExtensions = extensionsReducer(mergedExtensions, request);
92 }
93 var mergedOperationDefinition = {
94 kind: graphql.Kind.OPERATION_DEFINITION,
95 operation: requests[0].operationType,
96 variableDefinitions: mergedVariableDefinitions,
97 selectionSet: {
98 kind: graphql.Kind.SELECTION_SET,
99 selections: mergedSelections,
100 },
101 };
102 return {
103 document: {
104 kind: graphql.Kind.DOCUMENT,
105 definitions: tslib.__spreadArray([mergedOperationDefinition], tslib.__read(mergedFragmentDefinitions)),
106 },
107 variables: mergedVariables,
108 extensions: mergedExtensions,
109 context: requests[0].context,
110 info: requests[0].info,
111 operationType: requests[0].operationType,
112 };
113}
114function prefixRequest(prefix, request) {
115 var _a, e_2, _b;
116 var _c;
117 var executionVariables = (_c = request.variables) !== null && _c !== void 0 ? _c : {};
118 function prefixNode(node) {
119 return prefixNodeName(node, prefix);
120 }
121 var prefixedDocument = aliasTopLevelFields(prefix, request.document);
122 var executionVariableNames = Object.keys(executionVariables);
123 if (executionVariableNames.length > 0) {
124 prefixedDocument = graphql.visit(prefixedDocument, (_a = {},
125 _a[graphql.Kind.VARIABLE] = prefixNode,
126 _a[graphql.Kind.FRAGMENT_DEFINITION] = prefixNode,
127 _a[graphql.Kind.FRAGMENT_SPREAD] = prefixNode,
128 _a));
129 }
130 var prefixedVariables = {};
131 try {
132 for (var executionVariableNames_1 = tslib.__values(executionVariableNames), executionVariableNames_1_1 = executionVariableNames_1.next(); !executionVariableNames_1_1.done; executionVariableNames_1_1 = executionVariableNames_1.next()) {
133 var variableName = executionVariableNames_1_1.value;
134 prefixedVariables[prefix + variableName] = executionVariables[variableName];
135 }
136 }
137 catch (e_2_1) { e_2 = { error: e_2_1 }; }
138 finally {
139 try {
140 if (executionVariableNames_1_1 && !executionVariableNames_1_1.done && (_b = executionVariableNames_1.return)) _b.call(executionVariableNames_1);
141 }
142 finally { if (e_2) throw e_2.error; }
143 }
144 return {
145 document: prefixedDocument,
146 variables: prefixedVariables,
147 operationType: request.operationType,
148 };
149}
150/**
151 * Adds prefixed aliases to top-level fields of the query.
152 *
153 * @see aliasFieldsInSelection for implementation details
154 */
155function aliasTopLevelFields(prefix, document) {
156 var _a, _b;
157 var transformer = (_a = {},
158 _a[graphql.Kind.OPERATION_DEFINITION] = function (def) {
159 var selections = def.selectionSet.selections;
160 return tslib.__assign(tslib.__assign({}, def), { selectionSet: tslib.__assign(tslib.__assign({}, def.selectionSet), { selections: aliasFieldsInSelection(prefix, selections, document) }) });
161 },
162 _a);
163 return graphql.visit(document, transformer, (_b = {},
164 _b[graphql.Kind.DOCUMENT] = ["definitions"],
165 _b));
166}
167/**
168 * Add aliases to fields of the selection, including top-level fields of inline fragments.
169 * Fragment spreads are converted to inline fragments and their top-level fields are also aliased.
170 *
171 * Note that this method is shallow. It adds aliases only to the top-level fields and doesn't
172 * descend to field sub-selections.
173 *
174 * For example, transforms:
175 * {
176 * foo
177 * ... on Query { foo }
178 * ...FragmentWithBarField
179 * }
180 * To:
181 * {
182 * graphqlTools1_foo: foo
183 * ... on Query { graphqlTools1_foo: foo }
184 * ... on Query { graphqlTools1_bar: bar }
185 * }
186 */
187function aliasFieldsInSelection(prefix, selections, document) {
188 return selections.map(function (selection) {
189 switch (selection.kind) {
190 case graphql.Kind.INLINE_FRAGMENT:
191 return aliasFieldsInInlineFragment(prefix, selection, document);
192 case graphql.Kind.FRAGMENT_SPREAD: {
193 var inlineFragment = inlineFragmentSpread(selection, document);
194 return aliasFieldsInInlineFragment(prefix, inlineFragment, document);
195 }
196 case graphql.Kind.FIELD:
197 default:
198 return aliasField(selection, prefix);
199 }
200 });
201}
202/**
203 * Add aliases to top-level fields of the inline fragment.
204 * Returns new inline fragment node.
205 *
206 * For Example, transforms:
207 * ... on Query { foo, ... on Query { bar: foo } }
208 * To
209 * ... on Query { graphqlTools1_foo: foo, ... on Query { graphqlTools1_bar: foo } }
210 */
211function aliasFieldsInInlineFragment(prefix, fragment, document) {
212 var selections = fragment.selectionSet.selections;
213 return tslib.__assign(tslib.__assign({}, fragment), { selectionSet: tslib.__assign(tslib.__assign({}, fragment.selectionSet), { selections: aliasFieldsInSelection(prefix, selections, document) }) });
214}
215/**
216 * Replaces fragment spread with inline fragment
217 *
218 * Example:
219 * query { ...Spread }
220 * fragment Spread on Query { bar }
221 *
222 * Transforms to:
223 * query { ... on Query { bar } }
224 */
225function inlineFragmentSpread(spread, document) {
226 var fragment = document.definitions.find(function (def) { return isFragmentDefinition(def) && def.name.value === spread.name.value; });
227 if (!fragment) {
228 throw new Error("Fragment " + spread.name.value + " does not exist");
229 }
230 var typeCondition = fragment.typeCondition, selectionSet = fragment.selectionSet;
231 return {
232 kind: graphql.Kind.INLINE_FRAGMENT,
233 typeCondition: typeCondition,
234 selectionSet: selectionSet,
235 directives: spread.directives,
236 };
237}
238function prefixNodeName(namedNode, prefix) {
239 return tslib.__assign(tslib.__assign({}, namedNode), { name: tslib.__assign(tslib.__assign({}, namedNode.name), { value: prefix + namedNode.name.value }) });
240}
241/**
242 * Returns a new FieldNode with prefixed alias
243 *
244 * Example. Given prefix === "graphqlTools1_" transforms:
245 * { foo } -> { graphqlTools1_foo: foo }
246 * { foo: bar } -> { graphqlTools1_foo: bar }
247 */
248function aliasField(field, aliasPrefix) {
249 var aliasNode = field.alias ? field.alias : field.name;
250 return tslib.__assign(tslib.__assign({}, field), { alias: tslib.__assign(tslib.__assign({}, aliasNode), { value: aliasPrefix + aliasNode.value }) });
251}
252function isOperationDefinition(def) {
253 return def.kind === graphql.Kind.OPERATION_DEFINITION;
254}
255function isFragmentDefinition(def) {
256 return def.kind === graphql.Kind.FRAGMENT_DEFINITION;
257}
258
259// adapted from https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js
260/**
261 * Split and transform result of the query produced by the `merge` function
262 */
263function splitResult(_a, numResults) {
264 var _b, e_1, _c;
265 var data = _a.data, errors = _a.errors;
266 var splitResults = [];
267 for (var i = 0; i < numResults; i++) {
268 splitResults.push({});
269 }
270 if (data) {
271 for (var prefixedKey in data) {
272 var _d = parseKey(prefixedKey), index = _d.index, originalKey = _d.originalKey;
273 var result = splitResults[index];
274 if (result == null) {
275 continue;
276 }
277 if (result.data == null) {
278 result.data = (_b = {}, _b[originalKey] = data[prefixedKey], _b);
279 }
280 else {
281 result.data[originalKey] = data[prefixedKey];
282 }
283 }
284 }
285 if (errors) {
286 try {
287 for (var errors_1 = tslib.__values(errors), errors_1_1 = errors_1.next(); !errors_1_1.done; errors_1_1 = errors_1.next()) {
288 var error = errors_1_1.value;
289 if (error.path) {
290 var parsedKey = parseKey(error.path[0]);
291 var index = parsedKey.index, originalKey = parsedKey.originalKey;
292 var newError = utils.relocatedError(error, tslib.__spreadArray([originalKey], tslib.__read(error.path.slice(1))));
293 var errors_2 = (splitResults[index].errors = (splitResults[index].errors || []));
294 errors_2.push(newError);
295 }
296 }
297 }
298 catch (e_1_1) { e_1 = { error: e_1_1 }; }
299 finally {
300 try {
301 if (errors_1_1 && !errors_1_1.done && (_c = errors_1.return)) _c.call(errors_1);
302 }
303 finally { if (e_1) throw e_1.error; }
304 }
305 }
306 return splitResults;
307}
308
309function createBatchingExecutor(executor, dataLoaderOptions, extensionsReducer) {
310 if (extensionsReducer === void 0) { extensionsReducer = defaultExtensionsReducer; }
311 var loader = new DataLoader(createLoadFn(executor, extensionsReducer), dataLoaderOptions);
312 return function (request) {
313 return request.operationType === 'subscription' ? executor(request) : loader.load(request);
314 };
315}
316function createLoadFn(executor, extensionsReducer) {
317 return function batchExecuteLoadFn(requests) {
318 return tslib.__awaiter(this, void 0, void 0, function () {
319 var execBatches, index, request, currentBatch, operationType, currentOperationType, results;
320 var _this = this;
321 return tslib.__generator(this, function (_a) {
322 switch (_a.label) {
323 case 0:
324 execBatches = [];
325 index = 0;
326 request = requests[index];
327 currentBatch = [request];
328 execBatches.push(currentBatch);
329 operationType = request.operationType;
330 while (++index < requests.length) {
331 currentOperationType = requests[index].operationType;
332 if (operationType == null) {
333 throw new Error('Could not identify operation type of document.');
334 }
335 if (operationType === currentOperationType) {
336 currentBatch.push(requests[index]);
337 }
338 else {
339 currentBatch = [requests[index]];
340 execBatches.push(currentBatch);
341 }
342 }
343 return [4 /*yield*/, Promise.all(execBatches.map(function (execBatch) { return tslib.__awaiter(_this, void 0, void 0, function () {
344 var mergedRequests, resultBatches;
345 return tslib.__generator(this, function (_a) {
346 switch (_a.label) {
347 case 0:
348 mergedRequests = mergeRequests(execBatch, extensionsReducer);
349 return [4 /*yield*/, executor(mergedRequests)];
350 case 1:
351 resultBatches = (_a.sent());
352 return [2 /*return*/, splitResult(resultBatches, execBatch.length)];
353 }
354 });
355 }); }))];
356 case 1:
357 results = _a.sent();
358 return [2 /*return*/, results.flat()];
359 }
360 });
361 });
362 };
363}
364function defaultExtensionsReducer(mergedExtensions, request) {
365 var newExtensions = request.extensions;
366 if (newExtensions != null) {
367 Object.assign(mergedExtensions, newExtensions);
368 }
369 return mergedExtensions;
370}
371
372function memoize2of4(fn) {
373 var cache1;
374 function memoized(a1, a2, a3, a4) {
375 if (!cache1) {
376 cache1 = new WeakMap();
377 var cache2_1 = new WeakMap();
378 cache1.set(a1, cache2_1);
379 var newValue = fn(a1, a2, a3, a4);
380 cache2_1.set(a2, newValue);
381 return newValue;
382 }
383 var cache2 = cache1.get(a1);
384 if (!cache2) {
385 cache2 = new WeakMap();
386 cache1.set(a1, cache2);
387 var newValue = fn(a1, a2, a3, a4);
388 cache2.set(a2, newValue);
389 return newValue;
390 }
391 var cachedValue = cache2.get(a2);
392 if (cachedValue === undefined) {
393 var newValue = fn(a1, a2, a3, a4);
394 cache2.set(a2, newValue);
395 return newValue;
396 }
397 return cachedValue;
398 }
399 return memoized;
400}
401
402var getBatchingExecutor = memoize2of4(function getBatchingExecutor(_context, executor, dataLoaderOptions, extensionsReducer) {
403 return createBatchingExecutor(executor, dataLoaderOptions, extensionsReducer);
404});
405
406exports.createBatchingExecutor = createBatchingExecutor;
407exports.getBatchingExecutor = getBatchingExecutor;