UNPKG

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