UNPKG

60.5 kBJavaScriptView Raw
1import { __assign, __awaiter, __generator } from "tslib";
2import { invariant, newInvariantError } from "../utilities/globals/index.js";
3import { equal } from "@wry/equality";
4import { execute } from "../link/core/index.js";
5import { hasDirectives, isExecutionPatchIncrementalResult, isExecutionPatchResult, removeDirectivesFromDocument, } from "../utilities/index.js";
6import { canonicalStringify } from "../cache/index.js";
7import { getDefaultValues, getOperationDefinition, getOperationName, hasClientExports, graphQLResultHasError, getGraphQLErrorsFromResult, Observable, asyncMap, isNonEmptyArray, Concast, makeUniqueId, isDocumentNode, isNonNullObject, DocumentTransform, } from "../utilities/index.js";
8import { mergeIncrementalData } from "../utilities/common/incrementalResult.js";
9import { ApolloError, isApolloError, graphQLResultHasProtocolErrors, } from "../errors/index.js";
10import { ObservableQuery, logMissingFieldErrors } from "./ObservableQuery.js";
11import { NetworkStatus, isNetworkRequestInFlight } from "./networkStatus.js";
12import { QueryInfo, shouldWriteResult, } from "./QueryInfo.js";
13import { PROTOCOL_ERRORS_SYMBOL } from "../errors/index.js";
14import { print } from "../utilities/index.js";
15var hasOwnProperty = Object.prototype.hasOwnProperty;
16var IGNORE = Object.create(null);
17import { Trie } from "@wry/trie";
18import { AutoCleanedWeakCache, cacheSizes } from "../utilities/index.js";
19var QueryManager = /** @class */ (function () {
20 function QueryManager(options) {
21 var _this = this;
22 this.clientAwareness = {};
23 // All the queries that the QueryManager is currently managing (not
24 // including mutations and subscriptions).
25 this.queries = new Map();
26 // Maps from queryId strings to Promise rejection functions for
27 // currently active queries and fetches.
28 // Use protected instead of private field so
29 // @apollo/experimental-nextjs-app-support can access type info.
30 this.fetchCancelFns = new Map();
31 this.transformCache = new AutoCleanedWeakCache(cacheSizes["queryManager.getDocumentInfo"] ||
32 2000 /* defaultCacheSizes["queryManager.getDocumentInfo"] */);
33 this.queryIdCounter = 1;
34 this.requestIdCounter = 1;
35 this.mutationIdCounter = 1;
36 // Use protected instead of private field so
37 // @apollo/experimental-nextjs-app-support can access type info.
38 this.inFlightLinkObservables = new Trie(false);
39 var defaultDocumentTransform = new DocumentTransform(function (document) { return _this.cache.transformDocument(document); },
40 // Allow the apollo cache to manage its own transform caches
41 { cache: false });
42 this.cache = options.cache;
43 this.link = options.link;
44 this.defaultOptions = options.defaultOptions;
45 this.queryDeduplication = options.queryDeduplication;
46 this.clientAwareness = options.clientAwareness;
47 this.localState = options.localState;
48 this.ssrMode = options.ssrMode;
49 this.assumeImmutableResults = options.assumeImmutableResults;
50 var documentTransform = options.documentTransform;
51 this.documentTransform =
52 documentTransform ?
53 defaultDocumentTransform
54 .concat(documentTransform)
55 // The custom document transform may add new fragment spreads or new
56 // field selections, so we want to give the cache a chance to run
57 // again. For example, the InMemoryCache adds __typename to field
58 // selections and fragments from the fragment registry.
59 .concat(defaultDocumentTransform)
60 : defaultDocumentTransform;
61 this.defaultContext = options.defaultContext || Object.create(null);
62 if ((this.onBroadcast = options.onBroadcast)) {
63 this.mutationStore = Object.create(null);
64 }
65 }
66 /**
67 * Call this method to terminate any active query processes, making it safe
68 * to dispose of this QueryManager instance.
69 */
70 QueryManager.prototype.stop = function () {
71 var _this = this;
72 this.queries.forEach(function (_info, queryId) {
73 _this.stopQueryNoBroadcast(queryId);
74 });
75 this.cancelPendingFetches(newInvariantError(26));
76 };
77 QueryManager.prototype.cancelPendingFetches = function (error) {
78 this.fetchCancelFns.forEach(function (cancel) { return cancel(error); });
79 this.fetchCancelFns.clear();
80 };
81 QueryManager.prototype.mutate = function (_a) {
82 return __awaiter(this, arguments, void 0, function (_b) {
83 var mutationId, hasClientExports, mutationStoreValue, isOptimistic, self;
84 var _c, _d;
85 var mutation = _b.mutation, variables = _b.variables, optimisticResponse = _b.optimisticResponse, updateQueries = _b.updateQueries, _e = _b.refetchQueries, refetchQueries = _e === void 0 ? [] : _e, _f = _b.awaitRefetchQueries, awaitRefetchQueries = _f === void 0 ? false : _f, updateWithProxyFn = _b.update, onQueryUpdated = _b.onQueryUpdated, _g = _b.fetchPolicy, fetchPolicy = _g === void 0 ? ((_c = this.defaultOptions.mutate) === null || _c === void 0 ? void 0 : _c.fetchPolicy) || "network-only" : _g, _h = _b.errorPolicy, errorPolicy = _h === void 0 ? ((_d = this.defaultOptions.mutate) === null || _d === void 0 ? void 0 : _d.errorPolicy) || "none" : _h, keepRootFields = _b.keepRootFields, context = _b.context;
86 return __generator(this, function (_j) {
87 switch (_j.label) {
88 case 0:
89 invariant(mutation, 27);
90 invariant(fetchPolicy === "network-only" || fetchPolicy === "no-cache", 28);
91 mutationId = this.generateMutationId();
92 mutation = this.cache.transformForLink(this.transform(mutation));
93 hasClientExports = this.getDocumentInfo(mutation).hasClientExports;
94 variables = this.getVariables(mutation, variables);
95 if (!hasClientExports) return [3 /*break*/, 2];
96 return [4 /*yield*/, this.localState.addExportedVariables(mutation, variables, context)];
97 case 1:
98 variables = (_j.sent());
99 _j.label = 2;
100 case 2:
101 mutationStoreValue = this.mutationStore &&
102 (this.mutationStore[mutationId] = {
103 mutation: mutation,
104 variables: variables,
105 loading: true,
106 error: null,
107 });
108 isOptimistic = optimisticResponse &&
109 this.markMutationOptimistic(optimisticResponse, {
110 mutationId: mutationId,
111 document: mutation,
112 variables: variables,
113 fetchPolicy: fetchPolicy,
114 errorPolicy: errorPolicy,
115 context: context,
116 updateQueries: updateQueries,
117 update: updateWithProxyFn,
118 keepRootFields: keepRootFields,
119 });
120 this.broadcastQueries();
121 self = this;
122 return [2 /*return*/, new Promise(function (resolve, reject) {
123 return asyncMap(self.getObservableFromLink(mutation, __assign(__assign({}, context), { optimisticResponse: isOptimistic ? optimisticResponse : void 0 }), variables, {}, false), function (result) {
124 if (graphQLResultHasError(result) && errorPolicy === "none") {
125 throw new ApolloError({
126 graphQLErrors: getGraphQLErrorsFromResult(result),
127 });
128 }
129 if (mutationStoreValue) {
130 mutationStoreValue.loading = false;
131 mutationStoreValue.error = null;
132 }
133 var storeResult = __assign({}, result);
134 if (typeof refetchQueries === "function") {
135 refetchQueries = refetchQueries(storeResult);
136 }
137 if (errorPolicy === "ignore" && graphQLResultHasError(storeResult)) {
138 delete storeResult.errors;
139 }
140 return self.markMutationResult({
141 mutationId: mutationId,
142 result: storeResult,
143 document: mutation,
144 variables: variables,
145 fetchPolicy: fetchPolicy,
146 errorPolicy: errorPolicy,
147 context: context,
148 update: updateWithProxyFn,
149 updateQueries: updateQueries,
150 awaitRefetchQueries: awaitRefetchQueries,
151 refetchQueries: refetchQueries,
152 removeOptimistic: isOptimistic ? mutationId : void 0,
153 onQueryUpdated: onQueryUpdated,
154 keepRootFields: keepRootFields,
155 });
156 }).subscribe({
157 next: function (storeResult) {
158 self.broadcastQueries();
159 // Since mutations might receive multiple payloads from the
160 // ApolloLink chain (e.g. when used with @defer),
161 // we resolve with a SingleExecutionResult or after the final
162 // ExecutionPatchResult has arrived and we have assembled the
163 // multipart response into a single result.
164 if (!("hasNext" in storeResult) || storeResult.hasNext === false) {
165 resolve(storeResult);
166 }
167 },
168 error: function (err) {
169 if (mutationStoreValue) {
170 mutationStoreValue.loading = false;
171 mutationStoreValue.error = err;
172 }
173 if (isOptimistic) {
174 self.cache.removeOptimistic(mutationId);
175 }
176 self.broadcastQueries();
177 reject(err instanceof ApolloError ? err : (new ApolloError({
178 networkError: err,
179 })));
180 },
181 });
182 })];
183 }
184 });
185 });
186 };
187 QueryManager.prototype.markMutationResult = function (mutation, cache) {
188 var _this = this;
189 if (cache === void 0) { cache = this.cache; }
190 var result = mutation.result;
191 var cacheWrites = [];
192 var skipCache = mutation.fetchPolicy === "no-cache";
193 if (!skipCache && shouldWriteResult(result, mutation.errorPolicy)) {
194 if (!isExecutionPatchIncrementalResult(result)) {
195 cacheWrites.push({
196 result: result.data,
197 dataId: "ROOT_MUTATION",
198 query: mutation.document,
199 variables: mutation.variables,
200 });
201 }
202 if (isExecutionPatchIncrementalResult(result) &&
203 isNonEmptyArray(result.incremental)) {
204 var diff = cache.diff({
205 id: "ROOT_MUTATION",
206 // The cache complains if passed a mutation where it expects a
207 // query, so we transform mutations and subscriptions to queries
208 // (only once, thanks to this.transformCache).
209 query: this.getDocumentInfo(mutation.document).asQuery,
210 variables: mutation.variables,
211 optimistic: false,
212 returnPartialData: true,
213 });
214 var mergedData = void 0;
215 if (diff.result) {
216 mergedData = mergeIncrementalData(diff.result, result);
217 }
218 if (typeof mergedData !== "undefined") {
219 // cast the ExecutionPatchResult to FetchResult here since
220 // ExecutionPatchResult never has `data` when returned from the server
221 result.data = mergedData;
222 cacheWrites.push({
223 result: mergedData,
224 dataId: "ROOT_MUTATION",
225 query: mutation.document,
226 variables: mutation.variables,
227 });
228 }
229 }
230 var updateQueries_1 = mutation.updateQueries;
231 if (updateQueries_1) {
232 this.queries.forEach(function (_a, queryId) {
233 var observableQuery = _a.observableQuery;
234 var queryName = observableQuery && observableQuery.queryName;
235 if (!queryName || !hasOwnProperty.call(updateQueries_1, queryName)) {
236 return;
237 }
238 var updater = updateQueries_1[queryName];
239 var _b = _this.queries.get(queryId), document = _b.document, variables = _b.variables;
240 // Read the current query result from the store.
241 var _c = cache.diff({
242 query: document,
243 variables: variables,
244 returnPartialData: true,
245 optimistic: false,
246 }), currentQueryResult = _c.result, complete = _c.complete;
247 if (complete && currentQueryResult) {
248 // Run our reducer using the current query result and the mutation result.
249 var nextQueryResult = updater(currentQueryResult, {
250 mutationResult: result,
251 queryName: (document && getOperationName(document)) || void 0,
252 queryVariables: variables,
253 });
254 // Write the modified result back into the store if we got a new result.
255 if (nextQueryResult) {
256 cacheWrites.push({
257 result: nextQueryResult,
258 dataId: "ROOT_QUERY",
259 query: document,
260 variables: variables,
261 });
262 }
263 }
264 });
265 }
266 }
267 if (cacheWrites.length > 0 ||
268 (mutation.refetchQueries || "").length > 0 ||
269 mutation.update ||
270 mutation.onQueryUpdated ||
271 mutation.removeOptimistic) {
272 var results_1 = [];
273 this.refetchQueries({
274 updateCache: function (cache) {
275 if (!skipCache) {
276 cacheWrites.forEach(function (write) { return cache.write(write); });
277 }
278 // If the mutation has some writes associated with it then we need to
279 // apply those writes to the store by running this reducer again with
280 // a write action.
281 var update = mutation.update;
282 // Determine whether result is a SingleExecutionResult,
283 // or the final ExecutionPatchResult.
284 var isFinalResult = !isExecutionPatchResult(result) ||
285 (isExecutionPatchIncrementalResult(result) && !result.hasNext);
286 if (update) {
287 if (!skipCache) {
288 // Re-read the ROOT_MUTATION data we just wrote into the cache
289 // (the first cache.write call in the cacheWrites.forEach loop
290 // above), so field read functions have a chance to run for
291 // fields within mutation result objects.
292 var diff = cache.diff({
293 id: "ROOT_MUTATION",
294 // The cache complains if passed a mutation where it expects a
295 // query, so we transform mutations and subscriptions to queries
296 // (only once, thanks to this.transformCache).
297 query: _this.getDocumentInfo(mutation.document).asQuery,
298 variables: mutation.variables,
299 optimistic: false,
300 returnPartialData: true,
301 });
302 if (diff.complete) {
303 result = __assign(__assign({}, result), { data: diff.result });
304 if ("incremental" in result) {
305 delete result.incremental;
306 }
307 if ("hasNext" in result) {
308 delete result.hasNext;
309 }
310 }
311 }
312 // If we've received the whole response,
313 // either a SingleExecutionResult or the final ExecutionPatchResult,
314 // call the update function.
315 if (isFinalResult) {
316 update(cache, result, {
317 context: mutation.context,
318 variables: mutation.variables,
319 });
320 }
321 }
322 // TODO Do this with cache.evict({ id: 'ROOT_MUTATION' }) but make it
323 // shallow to allow rolling back optimistic evictions.
324 if (!skipCache && !mutation.keepRootFields && isFinalResult) {
325 cache.modify({
326 id: "ROOT_MUTATION",
327 fields: function (value, _a) {
328 var fieldName = _a.fieldName, DELETE = _a.DELETE;
329 return fieldName === "__typename" ? value : DELETE;
330 },
331 });
332 }
333 },
334 include: mutation.refetchQueries,
335 // Write the final mutation.result to the root layer of the cache.
336 optimistic: false,
337 // Remove the corresponding optimistic layer at the same time as we
338 // write the final non-optimistic result.
339 removeOptimistic: mutation.removeOptimistic,
340 // Let the caller of client.mutate optionally determine the refetching
341 // behavior for watched queries after the mutation.update function runs.
342 // If no onQueryUpdated function was provided for this mutation, pass
343 // null instead of undefined to disable the default refetching behavior.
344 onQueryUpdated: mutation.onQueryUpdated || null,
345 }).forEach(function (result) { return results_1.push(result); });
346 if (mutation.awaitRefetchQueries || mutation.onQueryUpdated) {
347 // Returning a promise here makes the mutation await that promise, so we
348 // include results in that promise's work if awaitRefetchQueries or an
349 // onQueryUpdated function was specified.
350 return Promise.all(results_1).then(function () { return result; });
351 }
352 }
353 return Promise.resolve(result);
354 };
355 QueryManager.prototype.markMutationOptimistic = function (optimisticResponse, mutation) {
356 var _this = this;
357 var data = typeof optimisticResponse === "function" ?
358 optimisticResponse(mutation.variables, { IGNORE: IGNORE })
359 : optimisticResponse;
360 if (data === IGNORE) {
361 return false;
362 }
363 this.cache.recordOptimisticTransaction(function (cache) {
364 try {
365 _this.markMutationResult(__assign(__assign({}, mutation), { result: { data: data } }), cache);
366 }
367 catch (error) {
368 globalThis.__DEV__ !== false && invariant.error(error);
369 }
370 }, mutation.mutationId);
371 return true;
372 };
373 QueryManager.prototype.fetchQuery = function (queryId, options, networkStatus) {
374 return this.fetchConcastWithInfo(queryId, options, networkStatus).concast
375 .promise;
376 };
377 QueryManager.prototype.getQueryStore = function () {
378 var store = Object.create(null);
379 this.queries.forEach(function (info, queryId) {
380 store[queryId] = {
381 variables: info.variables,
382 networkStatus: info.networkStatus,
383 networkError: info.networkError,
384 graphQLErrors: info.graphQLErrors,
385 };
386 });
387 return store;
388 };
389 QueryManager.prototype.resetErrors = function (queryId) {
390 var queryInfo = this.queries.get(queryId);
391 if (queryInfo) {
392 queryInfo.networkError = undefined;
393 queryInfo.graphQLErrors = [];
394 }
395 };
396 QueryManager.prototype.transform = function (document) {
397 return this.documentTransform.transformDocument(document);
398 };
399 QueryManager.prototype.getDocumentInfo = function (document) {
400 var transformCache = this.transformCache;
401 if (!transformCache.has(document)) {
402 var cacheEntry = {
403 // TODO These three calls (hasClientExports, shouldForceResolvers, and
404 // usesNonreactiveDirective) are performing independent full traversals
405 // of the transformed document. We should consider merging these
406 // traversals into a single pass in the future, though the work is
407 // cached after the first time.
408 hasClientExports: hasClientExports(document),
409 hasForcedResolvers: this.localState.shouldForceResolvers(document),
410 hasNonreactiveDirective: hasDirectives(["nonreactive"], document),
411 clientQuery: this.localState.clientQuery(document),
412 serverQuery: removeDirectivesFromDocument([
413 { name: "client", remove: true },
414 { name: "connection" },
415 { name: "nonreactive" },
416 ], document),
417 defaultVars: getDefaultValues(getOperationDefinition(document)),
418 // Transform any mutation or subscription operations to query operations
419 // so we can read/write them from/to the cache.
420 asQuery: __assign(__assign({}, document), { definitions: document.definitions.map(function (def) {
421 if (def.kind === "OperationDefinition" &&
422 def.operation !== "query") {
423 return __assign(__assign({}, def), { operation: "query" });
424 }
425 return def;
426 }) }),
427 };
428 transformCache.set(document, cacheEntry);
429 }
430 return transformCache.get(document);
431 };
432 QueryManager.prototype.getVariables = function (document, variables) {
433 return __assign(__assign({}, this.getDocumentInfo(document).defaultVars), variables);
434 };
435 QueryManager.prototype.watchQuery = function (options) {
436 var query = this.transform(options.query);
437 // assign variable default values if supplied
438 // NOTE: We don't modify options.query here with the transformed query to
439 // ensure observable.options.query is set to the raw untransformed query.
440 options = __assign(__assign({}, options), { variables: this.getVariables(query, options.variables) });
441 if (typeof options.notifyOnNetworkStatusChange === "undefined") {
442 options.notifyOnNetworkStatusChange = false;
443 }
444 var queryInfo = new QueryInfo(this);
445 var observable = new ObservableQuery({
446 queryManager: this,
447 queryInfo: queryInfo,
448 options: options,
449 });
450 observable["lastQuery"] = query;
451 this.queries.set(observable.queryId, queryInfo);
452 // We give queryInfo the transformed query to ensure the first cache diff
453 // uses the transformed query instead of the raw query
454 queryInfo.init({
455 document: query,
456 observableQuery: observable,
457 variables: observable.variables,
458 });
459 return observable;
460 };
461 QueryManager.prototype.query = function (options, queryId) {
462 var _this = this;
463 if (queryId === void 0) { queryId = this.generateQueryId(); }
464 invariant(options.query, 29);
465 invariant(options.query.kind === "Document", 30);
466 invariant(!options.returnPartialData, 31);
467 invariant(!options.pollInterval, 32);
468 return this.fetchQuery(queryId, __assign(__assign({}, options), { query: this.transform(options.query) })).finally(function () { return _this.stopQuery(queryId); });
469 };
470 QueryManager.prototype.generateQueryId = function () {
471 return String(this.queryIdCounter++);
472 };
473 QueryManager.prototype.generateRequestId = function () {
474 return this.requestIdCounter++;
475 };
476 QueryManager.prototype.generateMutationId = function () {
477 return String(this.mutationIdCounter++);
478 };
479 QueryManager.prototype.stopQueryInStore = function (queryId) {
480 this.stopQueryInStoreNoBroadcast(queryId);
481 this.broadcastQueries();
482 };
483 QueryManager.prototype.stopQueryInStoreNoBroadcast = function (queryId) {
484 var queryInfo = this.queries.get(queryId);
485 if (queryInfo)
486 queryInfo.stop();
487 };
488 QueryManager.prototype.clearStore = function (options) {
489 if (options === void 0) { options = {
490 discardWatches: true,
491 }; }
492 // Before we have sent the reset action to the store, we can no longer
493 // rely on the results returned by in-flight requests since these may
494 // depend on values that previously existed in the data portion of the
495 // store. So, we cancel the promises and observers that we have issued
496 // so far and not yet resolved (in the case of queries).
497 this.cancelPendingFetches(newInvariantError(33));
498 this.queries.forEach(function (queryInfo) {
499 if (queryInfo.observableQuery) {
500 // Set loading to true so listeners don't trigger unless they want
501 // results with partial data.
502 queryInfo.networkStatus = NetworkStatus.loading;
503 }
504 else {
505 queryInfo.stop();
506 }
507 });
508 if (this.mutationStore) {
509 this.mutationStore = Object.create(null);
510 }
511 // begin removing data from the store
512 return this.cache.reset(options);
513 };
514 QueryManager.prototype.getObservableQueries = function (include) {
515 var _this = this;
516 if (include === void 0) { include = "active"; }
517 var queries = new Map();
518 var queryNamesAndDocs = new Map();
519 var legacyQueryOptions = new Set();
520 if (Array.isArray(include)) {
521 include.forEach(function (desc) {
522 if (typeof desc === "string") {
523 queryNamesAndDocs.set(desc, false);
524 }
525 else if (isDocumentNode(desc)) {
526 queryNamesAndDocs.set(_this.transform(desc), false);
527 }
528 else if (isNonNullObject(desc) && desc.query) {
529 legacyQueryOptions.add(desc);
530 }
531 });
532 }
533 this.queries.forEach(function (_a, queryId) {
534 var oq = _a.observableQuery, document = _a.document;
535 if (oq) {
536 if (include === "all") {
537 queries.set(queryId, oq);
538 return;
539 }
540 var queryName = oq.queryName, fetchPolicy = oq.options.fetchPolicy;
541 if (fetchPolicy === "standby" ||
542 (include === "active" && !oq.hasObservers())) {
543 return;
544 }
545 if (include === "active" ||
546 (queryName && queryNamesAndDocs.has(queryName)) ||
547 (document && queryNamesAndDocs.has(document))) {
548 queries.set(queryId, oq);
549 if (queryName)
550 queryNamesAndDocs.set(queryName, true);
551 if (document)
552 queryNamesAndDocs.set(document, true);
553 }
554 }
555 });
556 if (legacyQueryOptions.size) {
557 legacyQueryOptions.forEach(function (options) {
558 // We will be issuing a fresh network request for this query, so we
559 // pre-allocate a new query ID here, using a special prefix to enable
560 // cleaning up these temporary queries later, after fetching.
561 var queryId = makeUniqueId("legacyOneTimeQuery");
562 var queryInfo = _this.getQuery(queryId).init({
563 document: options.query,
564 variables: options.variables,
565 });
566 var oq = new ObservableQuery({
567 queryManager: _this,
568 queryInfo: queryInfo,
569 options: __assign(__assign({}, options), { fetchPolicy: "network-only" }),
570 });
571 invariant(oq.queryId === queryId);
572 queryInfo.setObservableQuery(oq);
573 queries.set(queryId, oq);
574 });
575 }
576 if (globalThis.__DEV__ !== false && queryNamesAndDocs.size) {
577 queryNamesAndDocs.forEach(function (included, nameOrDoc) {
578 if (!included) {
579 globalThis.__DEV__ !== false && invariant.warn(typeof nameOrDoc === "string" ? 34 : 35, nameOrDoc);
580 }
581 });
582 }
583 return queries;
584 };
585 QueryManager.prototype.reFetchObservableQueries = function (includeStandby) {
586 var _this = this;
587 if (includeStandby === void 0) { includeStandby = false; }
588 var observableQueryPromises = [];
589 this.getObservableQueries(includeStandby ? "all" : "active").forEach(function (observableQuery, queryId) {
590 var fetchPolicy = observableQuery.options.fetchPolicy;
591 observableQuery.resetLastResults();
592 if (includeStandby ||
593 (fetchPolicy !== "standby" && fetchPolicy !== "cache-only")) {
594 observableQueryPromises.push(observableQuery.refetch());
595 }
596 _this.getQuery(queryId).setDiff(null);
597 });
598 this.broadcastQueries();
599 return Promise.all(observableQueryPromises);
600 };
601 QueryManager.prototype.setObservableQuery = function (observableQuery) {
602 this.getQuery(observableQuery.queryId).setObservableQuery(observableQuery);
603 };
604 QueryManager.prototype.startGraphQLSubscription = function (_a) {
605 var _this = this;
606 var query = _a.query, fetchPolicy = _a.fetchPolicy, _b = _a.errorPolicy, errorPolicy = _b === void 0 ? "none" : _b, variables = _a.variables, _c = _a.context, context = _c === void 0 ? {} : _c, _d = _a.extensions, extensions = _d === void 0 ? {} : _d;
607 query = this.transform(query);
608 variables = this.getVariables(query, variables);
609 var makeObservable = function (variables) {
610 return _this.getObservableFromLink(query, context, variables, extensions).map(function (result) {
611 if (fetchPolicy !== "no-cache") {
612 // the subscription interface should handle not sending us results we no longer subscribe to.
613 // XXX I don't think we ever send in an object with errors, but we might in the future...
614 if (shouldWriteResult(result, errorPolicy)) {
615 _this.cache.write({
616 query: query,
617 result: result.data,
618 dataId: "ROOT_SUBSCRIPTION",
619 variables: variables,
620 });
621 }
622 _this.broadcastQueries();
623 }
624 var hasErrors = graphQLResultHasError(result);
625 var hasProtocolErrors = graphQLResultHasProtocolErrors(result);
626 if (hasErrors || hasProtocolErrors) {
627 var errors = {};
628 if (hasErrors) {
629 errors.graphQLErrors = result.errors;
630 }
631 if (hasProtocolErrors) {
632 errors.protocolErrors = result.extensions[PROTOCOL_ERRORS_SYMBOL];
633 }
634 // `errorPolicy` is a mechanism for handling GraphQL errors, according
635 // to our documentation, so we throw protocol errors regardless of the
636 // set error policy.
637 if (errorPolicy === "none" || hasProtocolErrors) {
638 throw new ApolloError(errors);
639 }
640 }
641 if (errorPolicy === "ignore") {
642 delete result.errors;
643 }
644 return result;
645 });
646 };
647 if (this.getDocumentInfo(query).hasClientExports) {
648 var observablePromise_1 = this.localState
649 .addExportedVariables(query, variables, context)
650 .then(makeObservable);
651 return new Observable(function (observer) {
652 var sub = null;
653 observablePromise_1.then(function (observable) { return (sub = observable.subscribe(observer)); }, observer.error);
654 return function () { return sub && sub.unsubscribe(); };
655 });
656 }
657 return makeObservable(variables);
658 };
659 QueryManager.prototype.stopQuery = function (queryId) {
660 this.stopQueryNoBroadcast(queryId);
661 this.broadcastQueries();
662 };
663 QueryManager.prototype.stopQueryNoBroadcast = function (queryId) {
664 this.stopQueryInStoreNoBroadcast(queryId);
665 this.removeQuery(queryId);
666 };
667 QueryManager.prototype.removeQuery = function (queryId) {
668 // teardown all links
669 // Both `QueryManager.fetchRequest` and `QueryManager.query` create separate promises
670 // that each add their reject functions to fetchCancelFns.
671 // A query created with `QueryManager.query()` could trigger a `QueryManager.fetchRequest`.
672 // The same queryId could have two rejection fns for two promises
673 this.fetchCancelFns.delete(queryId);
674 if (this.queries.has(queryId)) {
675 this.getQuery(queryId).stop();
676 this.queries.delete(queryId);
677 }
678 };
679 QueryManager.prototype.broadcastQueries = function () {
680 if (this.onBroadcast)
681 this.onBroadcast();
682 this.queries.forEach(function (info) { return info.notify(); });
683 };
684 QueryManager.prototype.getLocalState = function () {
685 return this.localState;
686 };
687 QueryManager.prototype.getObservableFromLink = function (query, context, variables, extensions,
688 // Prefer context.queryDeduplication if specified.
689 deduplication) {
690 var _this = this;
691 var _a;
692 if (deduplication === void 0) { deduplication = (_a = context === null || context === void 0 ? void 0 : context.queryDeduplication) !== null && _a !== void 0 ? _a : this.queryDeduplication; }
693 var observable;
694 var _b = this.getDocumentInfo(query), serverQuery = _b.serverQuery, clientQuery = _b.clientQuery;
695 if (serverQuery) {
696 var _c = this, inFlightLinkObservables_1 = _c.inFlightLinkObservables, link = _c.link;
697 var operation = {
698 query: serverQuery,
699 variables: variables,
700 operationName: getOperationName(serverQuery) || void 0,
701 context: this.prepareContext(__assign(__assign({}, context), { forceFetch: !deduplication })),
702 extensions: extensions,
703 };
704 context = operation.context;
705 if (deduplication) {
706 var printedServerQuery_1 = print(serverQuery);
707 var varJson_1 = canonicalStringify(variables);
708 var entry = inFlightLinkObservables_1.lookup(printedServerQuery_1, varJson_1);
709 observable = entry.observable;
710 if (!observable) {
711 var concast = new Concast([
712 execute(link, operation),
713 ]);
714 observable = entry.observable = concast;
715 concast.beforeNext(function () {
716 inFlightLinkObservables_1.remove(printedServerQuery_1, varJson_1);
717 });
718 }
719 }
720 else {
721 observable = new Concast([
722 execute(link, operation),
723 ]);
724 }
725 }
726 else {
727 observable = new Concast([Observable.of({ data: {} })]);
728 context = this.prepareContext(context);
729 }
730 if (clientQuery) {
731 observable = asyncMap(observable, function (result) {
732 return _this.localState.runResolvers({
733 document: clientQuery,
734 remoteResult: result,
735 context: context,
736 variables: variables,
737 });
738 });
739 }
740 return observable;
741 };
742 QueryManager.prototype.getResultsFromLink = function (queryInfo, cacheWriteBehavior, options) {
743 var requestId = (queryInfo.lastRequestId = this.generateRequestId());
744 // Performing transformForLink here gives this.cache a chance to fill in
745 // missing fragment definitions (for example) before sending this document
746 // through the link chain.
747 var linkDocument = this.cache.transformForLink(options.query);
748 return asyncMap(this.getObservableFromLink(linkDocument, options.context, options.variables), function (result) {
749 var graphQLErrors = getGraphQLErrorsFromResult(result);
750 var hasErrors = graphQLErrors.length > 0;
751 var errorPolicy = options.errorPolicy;
752 // If we interrupted this request by calling getResultsFromLink again
753 // with the same QueryInfo object, we ignore the old results.
754 if (requestId >= queryInfo.lastRequestId) {
755 if (hasErrors && errorPolicy === "none") {
756 // Throwing here effectively calls observer.error.
757 throw queryInfo.markError(new ApolloError({
758 graphQLErrors: graphQLErrors,
759 }));
760 }
761 // Use linkDocument rather than queryInfo.document so the
762 // operation/fragments used to write the result are the same as the
763 // ones used to obtain it from the link.
764 queryInfo.markResult(result, linkDocument, options, cacheWriteBehavior);
765 queryInfo.markReady();
766 }
767 var aqr = {
768 data: result.data,
769 loading: false,
770 networkStatus: NetworkStatus.ready,
771 };
772 // In the case we start multiple network requests simulatenously, we
773 // want to ensure we properly set `data` if we're reporting on an old
774 // result which will not be caught by the conditional above that ends up
775 // throwing the markError result.
776 if (hasErrors && errorPolicy === "none") {
777 aqr.data = void 0;
778 }
779 if (hasErrors && errorPolicy !== "ignore") {
780 aqr.errors = graphQLErrors;
781 aqr.networkStatus = NetworkStatus.error;
782 }
783 return aqr;
784 }, function (networkError) {
785 var error = isApolloError(networkError) ? networkError : (new ApolloError({ networkError: networkError }));
786 // Avoid storing errors from older interrupted queries.
787 if (requestId >= queryInfo.lastRequestId) {
788 queryInfo.markError(error);
789 }
790 throw error;
791 });
792 };
793 QueryManager.prototype.fetchConcastWithInfo = function (queryId, options,
794 // The initial networkStatus for this fetch, most often
795 // NetworkStatus.loading, but also possibly fetchMore, poll, refetch,
796 // or setVariables.
797 networkStatus, query) {
798 var _this = this;
799 if (networkStatus === void 0) { networkStatus = NetworkStatus.loading; }
800 if (query === void 0) { query = options.query; }
801 var variables = this.getVariables(query, options.variables);
802 var queryInfo = this.getQuery(queryId);
803 var defaults = this.defaultOptions.watchQuery;
804 var _a = options.fetchPolicy, fetchPolicy = _a === void 0 ? (defaults && defaults.fetchPolicy) || "cache-first" : _a, _b = options.errorPolicy, errorPolicy = _b === void 0 ? (defaults && defaults.errorPolicy) || "none" : _b, _c = options.returnPartialData, returnPartialData = _c === void 0 ? false : _c, _d = options.notifyOnNetworkStatusChange, notifyOnNetworkStatusChange = _d === void 0 ? false : _d, _e = options.context, context = _e === void 0 ? {} : _e;
805 var normalized = Object.assign({}, options, {
806 query: query,
807 variables: variables,
808 fetchPolicy: fetchPolicy,
809 errorPolicy: errorPolicy,
810 returnPartialData: returnPartialData,
811 notifyOnNetworkStatusChange: notifyOnNetworkStatusChange,
812 context: context,
813 });
814 var fromVariables = function (variables) {
815 // Since normalized is always a fresh copy of options, it's safe to
816 // modify its properties here, rather than creating yet another new
817 // WatchQueryOptions object.
818 normalized.variables = variables;
819 var sourcesWithInfo = _this.fetchQueryByPolicy(queryInfo, normalized, networkStatus);
820 if (
821 // If we're in standby, postpone advancing options.fetchPolicy using
822 // applyNextFetchPolicy.
823 normalized.fetchPolicy !== "standby" &&
824 // The "standby" policy currently returns [] from fetchQueryByPolicy, so
825 // this is another way to detect when nothing was done/fetched.
826 sourcesWithInfo.sources.length > 0 &&
827 queryInfo.observableQuery) {
828 queryInfo.observableQuery["applyNextFetchPolicy"]("after-fetch", options);
829 }
830 return sourcesWithInfo;
831 };
832 // This cancel function needs to be set before the concast is created,
833 // in case concast creation synchronously cancels the request.
834 var cleanupCancelFn = function () { return _this.fetchCancelFns.delete(queryId); };
835 this.fetchCancelFns.set(queryId, function (reason) {
836 cleanupCancelFn();
837 // This delay ensures the concast variable has been initialized.
838 setTimeout(function () { return concast.cancel(reason); });
839 });
840 var concast, containsDataFromLink;
841 // If the query has @export(as: ...) directives, then we need to
842 // process those directives asynchronously. When there are no
843 // @export directives (the common case), we deliberately avoid
844 // wrapping the result of this.fetchQueryByPolicy in a Promise,
845 // since the timing of result delivery is (unfortunately) important
846 // for backwards compatibility. TODO This code could be simpler if
847 // we deprecated and removed LocalState.
848 if (this.getDocumentInfo(normalized.query).hasClientExports) {
849 concast = new Concast(this.localState
850 .addExportedVariables(normalized.query, normalized.variables, normalized.context)
851 .then(fromVariables)
852 .then(function (sourcesWithInfo) { return sourcesWithInfo.sources; }));
853 // there is just no way we can synchronously get the *right* value here,
854 // so we will assume `true`, which is the behaviour before the bug fix in
855 // #10597. This means that bug is not fixed in that case, and is probably
856 // un-fixable with reasonable effort for the edge case of @export as
857 // directives.
858 containsDataFromLink = true;
859 }
860 else {
861 var sourcesWithInfo = fromVariables(normalized.variables);
862 containsDataFromLink = sourcesWithInfo.fromLink;
863 concast = new Concast(sourcesWithInfo.sources);
864 }
865 concast.promise.then(cleanupCancelFn, cleanupCancelFn);
866 return {
867 concast: concast,
868 fromLink: containsDataFromLink,
869 };
870 };
871 QueryManager.prototype.refetchQueries = function (_a) {
872 var _this = this;
873 var updateCache = _a.updateCache, include = _a.include, _b = _a.optimistic, optimistic = _b === void 0 ? false : _b, _c = _a.removeOptimistic, removeOptimistic = _c === void 0 ? optimistic ? makeUniqueId("refetchQueries") : void 0 : _c, onQueryUpdated = _a.onQueryUpdated;
874 var includedQueriesById = new Map();
875 if (include) {
876 this.getObservableQueries(include).forEach(function (oq, queryId) {
877 includedQueriesById.set(queryId, {
878 oq: oq,
879 lastDiff: _this.getQuery(queryId).getDiff(),
880 });
881 });
882 }
883 var results = new Map();
884 if (updateCache) {
885 this.cache.batch({
886 update: updateCache,
887 // Since you can perform any combination of cache reads and/or writes in
888 // the cache.batch update function, its optimistic option can be either
889 // a boolean or a string, representing three distinct modes of
890 // operation:
891 //
892 // * false: read/write only the root layer
893 // * true: read/write the topmost layer
894 // * string: read/write a fresh optimistic layer with that ID string
895 //
896 // When typeof optimistic === "string", a new optimistic layer will be
897 // temporarily created within cache.batch with that string as its ID. If
898 // we then pass that same string as the removeOptimistic option, we can
899 // make cache.batch immediately remove the optimistic layer after
900 // running the updateCache function, triggering only one broadcast.
901 //
902 // However, the refetchQueries method accepts only true or false for its
903 // optimistic option (not string). We interpret true to mean a temporary
904 // optimistic layer should be created, to allow efficiently rolling back
905 // the effect of the updateCache function, which involves passing a
906 // string instead of true as the optimistic option to cache.batch, when
907 // refetchQueries receives optimistic: true.
908 //
909 // In other words, we are deliberately not supporting the use case of
910 // writing to an *existing* optimistic layer (using the refetchQueries
911 // updateCache function), since that would potentially interfere with
912 // other optimistic updates in progress. Instead, you can read/write
913 // only the root layer by passing optimistic: false to refetchQueries,
914 // or you can read/write a brand new optimistic layer that will be
915 // automatically removed by passing optimistic: true.
916 optimistic: (optimistic && removeOptimistic) || false,
917 // The removeOptimistic option can also be provided by itself, even if
918 // optimistic === false, to remove some previously-added optimistic
919 // layer safely and efficiently, like we do in markMutationResult.
920 //
921 // If an explicit removeOptimistic string is provided with optimistic:
922 // true, the removeOptimistic string will determine the ID of the
923 // temporary optimistic layer, in case that ever matters.
924 removeOptimistic: removeOptimistic,
925 onWatchUpdated: function (watch, diff, lastDiff) {
926 var oq = watch.watcher instanceof QueryInfo && watch.watcher.observableQuery;
927 if (oq) {
928 if (onQueryUpdated) {
929 // Since we're about to handle this query now, remove it from
930 // includedQueriesById, in case it was added earlier because of
931 // options.include.
932 includedQueriesById.delete(oq.queryId);
933 var result = onQueryUpdated(oq, diff, lastDiff);
934 if (result === true) {
935 // The onQueryUpdated function requested the default refetching
936 // behavior by returning true.
937 result = oq.refetch();
938 }
939 // Record the result in the results Map, as long as onQueryUpdated
940 // did not return false to skip/ignore this result.
941 if (result !== false) {
942 results.set(oq, result);
943 }
944 // Allow the default cache broadcast to happen, except when
945 // onQueryUpdated returns false.
946 return result;
947 }
948 if (onQueryUpdated !== null) {
949 // If we don't have an onQueryUpdated function, and onQueryUpdated
950 // was not disabled by passing null, make sure this query is
951 // "included" like any other options.include-specified query.
952 includedQueriesById.set(oq.queryId, { oq: oq, lastDiff: lastDiff, diff: diff });
953 }
954 }
955 },
956 });
957 }
958 if (includedQueriesById.size) {
959 includedQueriesById.forEach(function (_a, queryId) {
960 var oq = _a.oq, lastDiff = _a.lastDiff, diff = _a.diff;
961 var result;
962 // If onQueryUpdated is provided, we want to use it for all included
963 // queries, even the QueryOptions ones.
964 if (onQueryUpdated) {
965 if (!diff) {
966 var info = oq["queryInfo"];
967 info.reset(); // Force info.getDiff() to read from cache.
968 diff = info.getDiff();
969 }
970 result = onQueryUpdated(oq, diff, lastDiff);
971 }
972 // Otherwise, we fall back to refetching.
973 if (!onQueryUpdated || result === true) {
974 result = oq.refetch();
975 }
976 if (result !== false) {
977 results.set(oq, result);
978 }
979 if (queryId.indexOf("legacyOneTimeQuery") >= 0) {
980 _this.stopQueryNoBroadcast(queryId);
981 }
982 });
983 }
984 if (removeOptimistic) {
985 // In case no updateCache callback was provided (so cache.batch was not
986 // called above, and thus did not already remove the optimistic layer),
987 // remove it here. Since this is a no-op when the layer has already been
988 // removed, we do it even if we called cache.batch above, since it's
989 // possible this.cache is an instance of some ApolloCache subclass other
990 // than InMemoryCache, and does not fully support the removeOptimistic
991 // option for cache.batch.
992 this.cache.removeOptimistic(removeOptimistic);
993 }
994 return results;
995 };
996 QueryManager.prototype.fetchQueryByPolicy = function (queryInfo, _a,
997 // The initial networkStatus for this fetch, most often
998 // NetworkStatus.loading, but also possibly fetchMore, poll, refetch,
999 // or setVariables.
1000 networkStatus) {
1001 var _this = this;
1002 var query = _a.query, variables = _a.variables, fetchPolicy = _a.fetchPolicy, refetchWritePolicy = _a.refetchWritePolicy, errorPolicy = _a.errorPolicy, returnPartialData = _a.returnPartialData, context = _a.context, notifyOnNetworkStatusChange = _a.notifyOnNetworkStatusChange;
1003 var oldNetworkStatus = queryInfo.networkStatus;
1004 queryInfo.init({
1005 document: query,
1006 variables: variables,
1007 networkStatus: networkStatus,
1008 });
1009 var readCache = function () { return queryInfo.getDiff(); };
1010 var resultsFromCache = function (diff, networkStatus) {
1011 if (networkStatus === void 0) { networkStatus = queryInfo.networkStatus || NetworkStatus.loading; }
1012 var data = diff.result;
1013 if (globalThis.__DEV__ !== false && !returnPartialData && !equal(data, {})) {
1014 logMissingFieldErrors(diff.missing);
1015 }
1016 var fromData = function (data) {
1017 return Observable.of(__assign({ data: data, loading: isNetworkRequestInFlight(networkStatus), networkStatus: networkStatus }, (diff.complete ? null : { partial: true })));
1018 };
1019 if (data && _this.getDocumentInfo(query).hasForcedResolvers) {
1020 return _this.localState
1021 .runResolvers({
1022 document: query,
1023 remoteResult: { data: data },
1024 context: context,
1025 variables: variables,
1026 onlyRunForcedResolvers: true,
1027 })
1028 .then(function (resolved) { return fromData(resolved.data || void 0); });
1029 }
1030 // Resolves https://github.com/apollographql/apollo-client/issues/10317.
1031 // If errorPolicy is 'none' and notifyOnNetworkStatusChange is true,
1032 // data was incorrectly returned from the cache on refetch:
1033 // if diff.missing exists, we should not return cache data.
1034 if (errorPolicy === "none" &&
1035 networkStatus === NetworkStatus.refetch &&
1036 Array.isArray(diff.missing)) {
1037 return fromData(void 0);
1038 }
1039 return fromData(data);
1040 };
1041 var cacheWriteBehavior = fetchPolicy === "no-cache" ? 0 /* CacheWriteBehavior.FORBID */
1042 // Watched queries must opt into overwriting existing data on refetch,
1043 // by passing refetchWritePolicy: "overwrite" in their WatchQueryOptions.
1044 : (networkStatus === NetworkStatus.refetch &&
1045 refetchWritePolicy !== "merge") ?
1046 1 /* CacheWriteBehavior.OVERWRITE */
1047 : 2 /* CacheWriteBehavior.MERGE */;
1048 var resultsFromLink = function () {
1049 return _this.getResultsFromLink(queryInfo, cacheWriteBehavior, {
1050 query: query,
1051 variables: variables,
1052 context: context,
1053 fetchPolicy: fetchPolicy,
1054 errorPolicy: errorPolicy,
1055 });
1056 };
1057 var shouldNotify = notifyOnNetworkStatusChange &&
1058 typeof oldNetworkStatus === "number" &&
1059 oldNetworkStatus !== networkStatus &&
1060 isNetworkRequestInFlight(networkStatus);
1061 switch (fetchPolicy) {
1062 default:
1063 case "cache-first": {
1064 var diff = readCache();
1065 if (diff.complete) {
1066 return {
1067 fromLink: false,
1068 sources: [resultsFromCache(diff, queryInfo.markReady())],
1069 };
1070 }
1071 if (returnPartialData || shouldNotify) {
1072 return {
1073 fromLink: true,
1074 sources: [resultsFromCache(diff), resultsFromLink()],
1075 };
1076 }
1077 return { fromLink: true, sources: [resultsFromLink()] };
1078 }
1079 case "cache-and-network": {
1080 var diff = readCache();
1081 if (diff.complete || returnPartialData || shouldNotify) {
1082 return {
1083 fromLink: true,
1084 sources: [resultsFromCache(diff), resultsFromLink()],
1085 };
1086 }
1087 return { fromLink: true, sources: [resultsFromLink()] };
1088 }
1089 case "cache-only":
1090 return {
1091 fromLink: false,
1092 sources: [resultsFromCache(readCache(), queryInfo.markReady())],
1093 };
1094 case "network-only":
1095 if (shouldNotify) {
1096 return {
1097 fromLink: true,
1098 sources: [resultsFromCache(readCache()), resultsFromLink()],
1099 };
1100 }
1101 return { fromLink: true, sources: [resultsFromLink()] };
1102 case "no-cache":
1103 if (shouldNotify) {
1104 return {
1105 fromLink: true,
1106 // Note that queryInfo.getDiff() for no-cache queries does not call
1107 // cache.diff, but instead returns a { complete: false } stub result
1108 // when there is no queryInfo.diff already defined.
1109 sources: [resultsFromCache(queryInfo.getDiff()), resultsFromLink()],
1110 };
1111 }
1112 return { fromLink: true, sources: [resultsFromLink()] };
1113 case "standby":
1114 return { fromLink: false, sources: [] };
1115 }
1116 };
1117 QueryManager.prototype.getQuery = function (queryId) {
1118 if (queryId && !this.queries.has(queryId)) {
1119 this.queries.set(queryId, new QueryInfo(this, queryId));
1120 }
1121 return this.queries.get(queryId);
1122 };
1123 QueryManager.prototype.prepareContext = function (context) {
1124 if (context === void 0) { context = {}; }
1125 var newContext = this.localState.prepareContext(context);
1126 return __assign(__assign(__assign({}, this.defaultContext), newContext), { clientAwareness: this.clientAwareness });
1127 };
1128 return QueryManager;
1129}());
1130export { QueryManager };
1131//# sourceMappingURL=QueryManager.js.map
\No newline at end of file