UNPKG

42.7 kBJavaScriptView Raw
1import { __assign, __extends } from "tslib";
2import { invariant } from "../utilities/globals/index.js";
3import { equal } from "@wry/equality";
4import { NetworkStatus, isNetworkRequestInFlight } from "./networkStatus.js";
5import { cloneDeep, compact, getOperationDefinition, Observable, iterateObserversSafely, fixObservableSubclass, getQueryDefinition, } from "../utilities/index.js";
6import { ApolloError, isApolloError } from "../errors/index.js";
7import { equalByQuery } from "./equalByQuery.js";
8var assign = Object.assign, hasOwnProperty = Object.hasOwnProperty;
9var ObservableQuery = /** @class */ (function (_super) {
10 __extends(ObservableQuery, _super);
11 function ObservableQuery(_a) {
12 var queryManager = _a.queryManager, queryInfo = _a.queryInfo, options = _a.options;
13 var _this = _super.call(this, function (observer) {
14 // Zen Observable has its own error function, so in order to log correctly
15 // we need to provide a custom error callback.
16 try {
17 var subObserver = observer._subscription._observer;
18 if (subObserver && !subObserver.error) {
19 subObserver.error = defaultSubscriptionObserverErrorCallback;
20 }
21 }
22 catch (_a) { }
23 var first = !_this.observers.size;
24 _this.observers.add(observer);
25 // Deliver most recent error or result.
26 var last = _this.last;
27 if (last && last.error) {
28 observer.error && observer.error(last.error);
29 }
30 else if (last && last.result) {
31 observer.next && observer.next(last.result);
32 }
33 // Initiate observation of this query if it hasn't been reported to
34 // the QueryManager yet.
35 if (first) {
36 // Blindly catching here prevents unhandled promise rejections,
37 // and is safe because the ObservableQuery handles this error with
38 // this.observer.error, so we're not just swallowing the error by
39 // ignoring it here.
40 _this.reobserve().catch(function () { });
41 }
42 return function () {
43 if (_this.observers.delete(observer) && !_this.observers.size) {
44 _this.tearDownQuery();
45 }
46 };
47 }) || this;
48 _this.observers = new Set();
49 _this.subscriptions = new Set();
50 // related classes
51 _this.queryInfo = queryInfo;
52 _this.queryManager = queryManager;
53 // active state
54 _this.waitForOwnResult = skipCacheDataFor(options.fetchPolicy);
55 _this.isTornDown = false;
56 _this.subscribeToMore = _this.subscribeToMore.bind(_this);
57 var _b = queryManager.defaultOptions.watchQuery, _c = _b === void 0 ? {} : _b, _d = _c.fetchPolicy, defaultFetchPolicy = _d === void 0 ? "cache-first" : _d;
58 var _e = options.fetchPolicy, fetchPolicy = _e === void 0 ? defaultFetchPolicy : _e,
59 // Make sure we don't store "standby" as the initialFetchPolicy.
60 _f = options.initialFetchPolicy,
61 // Make sure we don't store "standby" as the initialFetchPolicy.
62 initialFetchPolicy = _f === void 0 ? fetchPolicy === "standby" ? defaultFetchPolicy : (fetchPolicy) : _f;
63 _this.options = __assign(__assign({}, options), {
64 // Remember the initial options.fetchPolicy so we can revert back to this
65 // policy when variables change. This information can also be specified
66 // (or overridden) by providing options.initialFetchPolicy explicitly.
67 initialFetchPolicy: initialFetchPolicy,
68 // This ensures this.options.fetchPolicy always has a string value, in
69 // case options.fetchPolicy was not provided.
70 fetchPolicy: fetchPolicy });
71 _this.queryId = queryInfo.queryId || queryManager.generateQueryId();
72 var opDef = getOperationDefinition(_this.query);
73 _this.queryName = opDef && opDef.name && opDef.name.value;
74 return _this;
75 }
76 Object.defineProperty(ObservableQuery.prototype, "query", {
77 // The `query` computed property will always reflect the document transformed
78 // by the last run query. `this.options.query` will always reflect the raw
79 // untransformed query to ensure document transforms with runtime conditionals
80 // are run on the original document.
81 get: function () {
82 return this.lastQuery || this.options.query;
83 },
84 enumerable: false,
85 configurable: true
86 });
87 Object.defineProperty(ObservableQuery.prototype, "variables", {
88 // Computed shorthand for this.options.variables, preserved for
89 // backwards compatibility.
90 /**
91 * An object containing the variables that were provided for the query.
92 */
93 get: function () {
94 return this.options.variables;
95 },
96 enumerable: false,
97 configurable: true
98 });
99 ObservableQuery.prototype.result = function () {
100 var _this = this;
101 return new Promise(function (resolve, reject) {
102 // TODO: this code doesn’t actually make sense insofar as the observer
103 // will never exist in this.observers due how zen-observable wraps observables.
104 // https://github.com/zenparsing/zen-observable/blob/master/src/Observable.js#L169
105 var observer = {
106 next: function (result) {
107 resolve(result);
108 // Stop the query within the QueryManager if we can before
109 // this function returns.
110 //
111 // We do this in order to prevent observers piling up within
112 // the QueryManager. Notice that we only fully unsubscribe
113 // from the subscription in a setTimeout(..., 0) call. This call can
114 // actually be handled by the browser at a much later time. If queries
115 // are fired in the meantime, observers that should have been removed
116 // from the QueryManager will continue to fire, causing an unnecessary
117 // performance hit.
118 _this.observers.delete(observer);
119 if (!_this.observers.size) {
120 _this.queryManager.removeQuery(_this.queryId);
121 }
122 setTimeout(function () {
123 subscription.unsubscribe();
124 }, 0);
125 },
126 error: reject,
127 };
128 var subscription = _this.subscribe(observer);
129 });
130 };
131 /** @internal */
132 ObservableQuery.prototype.resetDiff = function () {
133 this.queryInfo.resetDiff();
134 };
135 ObservableQuery.prototype.getCurrentResult = function (saveAsLastResult) {
136 if (saveAsLastResult === void 0) { saveAsLastResult = true; }
137 // Use the last result as long as the variables match this.variables.
138 var lastResult = this.getLastResult(true);
139 var networkStatus = this.queryInfo.networkStatus ||
140 (lastResult && lastResult.networkStatus) ||
141 NetworkStatus.ready;
142 var result = __assign(__assign({}, lastResult), { loading: isNetworkRequestInFlight(networkStatus), networkStatus: networkStatus });
143 var _a = this.options.fetchPolicy, fetchPolicy = _a === void 0 ? "cache-first" : _a;
144 if (
145 // These fetch policies should never deliver data from the cache, unless
146 // redelivering a previously delivered result.
147 skipCacheDataFor(fetchPolicy) ||
148 // If this.options.query has @client(always: true) fields, we cannot
149 // trust diff.result, since it was read from the cache without running
150 // local resolvers (and it's too late to run resolvers now, since we must
151 // return a result synchronously).
152 this.queryManager.getDocumentInfo(this.query).hasForcedResolvers) {
153 // Fall through.
154 }
155 else if (this.waitForOwnResult) {
156 // This would usually be a part of `QueryInfo.getDiff()`.
157 // which we skip in the waitForOwnResult case since we are not
158 // interested in the diff.
159 this.queryInfo["updateWatch"]();
160 }
161 else {
162 var diff = this.queryInfo.getDiff();
163 if (diff.complete || this.options.returnPartialData) {
164 result.data = diff.result;
165 }
166 if (equal(result.data, {})) {
167 result.data = void 0;
168 }
169 if (diff.complete) {
170 // Similar to setting result.partial to false, but taking advantage of the
171 // falsiness of missing fields.
172 delete result.partial;
173 // If the diff is complete, and we're using a FetchPolicy that
174 // terminates after a complete cache read, we can assume the next result
175 // we receive will have NetworkStatus.ready and !loading.
176 if (diff.complete &&
177 result.networkStatus === NetworkStatus.loading &&
178 (fetchPolicy === "cache-first" || fetchPolicy === "cache-only")) {
179 result.networkStatus = NetworkStatus.ready;
180 result.loading = false;
181 }
182 }
183 else {
184 result.partial = true;
185 }
186 if (globalThis.__DEV__ !== false &&
187 !diff.complete &&
188 !this.options.partialRefetch &&
189 !result.loading &&
190 !result.data &&
191 !result.error) {
192 logMissingFieldErrors(diff.missing);
193 }
194 }
195 if (saveAsLastResult) {
196 this.updateLastResult(result);
197 }
198 return result;
199 };
200 // Compares newResult to the snapshot we took of this.lastResult when it was
201 // first received.
202 ObservableQuery.prototype.isDifferentFromLastResult = function (newResult, variables) {
203 if (!this.last) {
204 return true;
205 }
206 var resultIsDifferent = this.queryManager.getDocumentInfo(this.query).hasNonreactiveDirective ?
207 !equalByQuery(this.query, this.last.result, newResult, this.variables)
208 : !equal(this.last.result, newResult);
209 return (resultIsDifferent || (variables && !equal(this.last.variables, variables)));
210 };
211 ObservableQuery.prototype.getLast = function (key, variablesMustMatch) {
212 var last = this.last;
213 if (last &&
214 last[key] &&
215 (!variablesMustMatch || equal(last.variables, this.variables))) {
216 return last[key];
217 }
218 };
219 ObservableQuery.prototype.getLastResult = function (variablesMustMatch) {
220 return this.getLast("result", variablesMustMatch);
221 };
222 ObservableQuery.prototype.getLastError = function (variablesMustMatch) {
223 return this.getLast("error", variablesMustMatch);
224 };
225 ObservableQuery.prototype.resetLastResults = function () {
226 delete this.last;
227 this.isTornDown = false;
228 };
229 ObservableQuery.prototype.resetQueryStoreErrors = function () {
230 this.queryManager.resetErrors(this.queryId);
231 };
232 /**
233 * Update the variables of this observable query, and fetch the new results.
234 * This method should be preferred over `setVariables` in most use cases.
235 *
236 * @param variables - The new set of variables. If there are missing variables,
237 * the previous values of those variables will be used.
238 */
239 ObservableQuery.prototype.refetch = function (variables) {
240 var _a;
241 var reobserveOptions = {
242 // Always disable polling for refetches.
243 pollInterval: 0,
244 };
245 // Unless the provided fetchPolicy always consults the network
246 // (no-cache, network-only, or cache-and-network), override it with
247 // network-only to force the refetch for this fetchQuery call.
248 var fetchPolicy = this.options.fetchPolicy;
249 if (fetchPolicy === "cache-and-network") {
250 reobserveOptions.fetchPolicy = fetchPolicy;
251 }
252 else if (fetchPolicy === "no-cache") {
253 reobserveOptions.fetchPolicy = "no-cache";
254 }
255 else {
256 reobserveOptions.fetchPolicy = "network-only";
257 }
258 if (globalThis.__DEV__ !== false && variables && hasOwnProperty.call(variables, "variables")) {
259 var queryDef = getQueryDefinition(this.query);
260 var vars = queryDef.variableDefinitions;
261 if (!vars || !vars.some(function (v) { return v.variable.name.value === "variables"; })) {
262 globalThis.__DEV__ !== false && invariant.warn(
263 20,
264 variables,
265 ((_a = queryDef.name) === null || _a === void 0 ? void 0 : _a.value) || queryDef
266 );
267 }
268 }
269 if (variables && !equal(this.options.variables, variables)) {
270 // Update the existing options with new variables
271 reobserveOptions.variables = this.options.variables = __assign(__assign({}, this.options.variables), variables);
272 }
273 this.queryInfo.resetLastWrite();
274 return this.reobserve(reobserveOptions, NetworkStatus.refetch);
275 };
276 /**
277 * A function that helps you fetch the next set of results for a [paginated list field](https://www.apollographql.com/docs/react/pagination/core-api/).
278 */
279 ObservableQuery.prototype.fetchMore = function (fetchMoreOptions) {
280 var _this = this;
281 var combinedOptions = __assign(__assign({}, (fetchMoreOptions.query ? fetchMoreOptions : (__assign(__assign(__assign(__assign({}, this.options), { query: this.options.query }), fetchMoreOptions), { variables: __assign(__assign({}, this.options.variables), fetchMoreOptions.variables) })))), {
282 // The fetchMore request goes immediately to the network and does
283 // not automatically write its result to the cache (hence no-cache
284 // instead of network-only), because we allow the caller of
285 // fetchMore to provide an updateQuery callback that determines how
286 // the data gets written to the cache.
287 fetchPolicy: "no-cache" });
288 combinedOptions.query = this.transformDocument(combinedOptions.query);
289 var qid = this.queryManager.generateQueryId();
290 // If a temporary query is passed to `fetchMore`, we don't want to store
291 // it as the last query result since it may be an optimized query for
292 // pagination. We will however run the transforms on the original document
293 // as well as the document passed in `fetchMoreOptions` to ensure the cache
294 // uses the most up-to-date document which may rely on runtime conditionals.
295 this.lastQuery =
296 fetchMoreOptions.query ?
297 this.transformDocument(this.options.query)
298 : combinedOptions.query;
299 // Simulate a loading result for the original query with
300 // result.networkStatus === NetworkStatus.fetchMore.
301 var queryInfo = this.queryInfo;
302 var originalNetworkStatus = queryInfo.networkStatus;
303 queryInfo.networkStatus = NetworkStatus.fetchMore;
304 if (combinedOptions.notifyOnNetworkStatusChange) {
305 this.observe();
306 }
307 var updatedQuerySet = new Set();
308 var updateQuery = fetchMoreOptions === null || fetchMoreOptions === void 0 ? void 0 : fetchMoreOptions.updateQuery;
309 var isCached = this.options.fetchPolicy !== "no-cache";
310 if (!isCached) {
311 invariant(updateQuery, 21);
312 }
313 return this.queryManager
314 .fetchQuery(qid, combinedOptions, NetworkStatus.fetchMore)
315 .then(function (fetchMoreResult) {
316 _this.queryManager.removeQuery(qid);
317 if (queryInfo.networkStatus === NetworkStatus.fetchMore) {
318 queryInfo.networkStatus = originalNetworkStatus;
319 }
320 if (isCached) {
321 // Performing this cache update inside a cache.batch transaction ensures
322 // any affected cache.watch watchers are notified at most once about any
323 // updates. Most watchers will be using the QueryInfo class, which
324 // responds to notifications by calling reobserveCacheFirst to deliver
325 // fetchMore cache results back to this ObservableQuery.
326 _this.queryManager.cache.batch({
327 update: function (cache) {
328 var updateQuery = fetchMoreOptions.updateQuery;
329 if (updateQuery) {
330 cache.updateQuery({
331 query: _this.query,
332 variables: _this.variables,
333 returnPartialData: true,
334 optimistic: false,
335 }, function (previous) {
336 return updateQuery(previous, {
337 fetchMoreResult: fetchMoreResult.data,
338 variables: combinedOptions.variables,
339 });
340 });
341 }
342 else {
343 // If we're using a field policy instead of updateQuery, the only
344 // thing we need to do is write the new data to the cache using
345 // combinedOptions.variables (instead of this.variables, which is
346 // what this.updateQuery uses, because it works by abusing the
347 // original field value, keyed by the original variables).
348 cache.writeQuery({
349 query: combinedOptions.query,
350 variables: combinedOptions.variables,
351 data: fetchMoreResult.data,
352 });
353 }
354 },
355 onWatchUpdated: function (watch) {
356 // Record the DocumentNode associated with any watched query whose
357 // data were updated by the cache writes above.
358 updatedQuerySet.add(watch.query);
359 },
360 });
361 }
362 else {
363 // There is a possibility `lastResult` may not be set when
364 // `fetchMore` is called which would cause this to crash. This should
365 // only happen if we haven't previously reported a result. We don't
366 // quite know what the right behavior should be here since this block
367 // of code runs after the fetch result has executed on the network.
368 // We plan to let it crash in the meantime.
369 //
370 // If we get bug reports due to the `data` property access on
371 // undefined, this should give us a real-world scenario that we can
372 // use to test against and determine the right behavior. If we do end
373 // up changing this behavior, this may require, for example, an
374 // adjustment to the types on `updateQuery` since that function
375 // expects that the first argument always contains previous result
376 // data, but not `undefined`.
377 var lastResult = _this.getLast("result");
378 var data = updateQuery(lastResult.data, {
379 fetchMoreResult: fetchMoreResult.data,
380 variables: combinedOptions.variables,
381 });
382 _this.reportResult(__assign(__assign({}, lastResult), { data: data }), _this.variables);
383 }
384 return fetchMoreResult;
385 })
386 .finally(function () {
387 // In case the cache writes above did not generate a broadcast
388 // notification (which would have been intercepted by onWatchUpdated),
389 // likely because the written data were the same as what was already in
390 // the cache, we still want fetchMore to deliver its final loading:false
391 // result with the unchanged data.
392 if (isCached && !updatedQuerySet.has(_this.query)) {
393 reobserveCacheFirst(_this);
394 }
395 });
396 };
397 // XXX the subscription variables are separate from the query variables.
398 // if you want to update subscription variables, right now you have to do that separately,
399 // and you can only do it by stopping the subscription and then subscribing again with new variables.
400 /**
401 * A function that enables you to execute a [subscription](https://www.apollographql.com/docs/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query.
402 *
403 * This function returns _another_ function that you can call to terminate the subscription.
404 */
405 ObservableQuery.prototype.subscribeToMore = function (options) {
406 var _this = this;
407 var subscription = this.queryManager
408 .startGraphQLSubscription({
409 query: options.document,
410 variables: options.variables,
411 context: options.context,
412 })
413 .subscribe({
414 next: function (subscriptionData) {
415 var updateQuery = options.updateQuery;
416 if (updateQuery) {
417 _this.updateQuery(function (previous, _a) {
418 var variables = _a.variables;
419 return updateQuery(previous, {
420 subscriptionData: subscriptionData,
421 variables: variables,
422 });
423 });
424 }
425 },
426 error: function (err) {
427 if (options.onError) {
428 options.onError(err);
429 return;
430 }
431 globalThis.__DEV__ !== false && invariant.error(22, err);
432 },
433 });
434 this.subscriptions.add(subscription);
435 return function () {
436 if (_this.subscriptions.delete(subscription)) {
437 subscription.unsubscribe();
438 }
439 };
440 };
441 ObservableQuery.prototype.setOptions = function (newOptions) {
442 return this.reobserve(newOptions);
443 };
444 ObservableQuery.prototype.silentSetOptions = function (newOptions) {
445 var mergedOptions = compact(this.options, newOptions || {});
446 assign(this.options, mergedOptions);
447 };
448 /**
449 * Update the variables of this observable query, and fetch the new results
450 * if they've changed. Most users should prefer `refetch` instead of
451 * `setVariables` in order to to be properly notified of results even when
452 * they come from the cache.
453 *
454 * Note: the `next` callback will *not* fire if the variables have not changed
455 * or if the result is coming from cache.
456 *
457 * Note: the promise will return the old results immediately if the variables
458 * have not changed.
459 *
460 * Note: the promise will return null immediately if the query is not active
461 * (there are no subscribers).
462 *
463 * @param variables - The new set of variables. If there are missing variables,
464 * the previous values of those variables will be used.
465 */
466 ObservableQuery.prototype.setVariables = function (variables) {
467 if (equal(this.variables, variables)) {
468 // If we have no observers, then we don't actually want to make a network
469 // request. As soon as someone observes the query, the request will kick
470 // off. For now, we just store any changes. (See #1077)
471 return this.observers.size ? this.result() : Promise.resolve();
472 }
473 this.options.variables = variables;
474 // See comment above
475 if (!this.observers.size) {
476 return Promise.resolve();
477 }
478 return this.reobserve({
479 // Reset options.fetchPolicy to its original value.
480 fetchPolicy: this.options.initialFetchPolicy,
481 variables: variables,
482 }, NetworkStatus.setVariables);
483 };
484 /**
485 * A function that enables you to update the query's cached result without executing a followup GraphQL operation.
486 *
487 * See [using updateQuery and updateFragment](https://www.apollographql.com/docs/react/caching/cache-interaction/#using-updatequery-and-updatefragment) for additional information.
488 */
489 ObservableQuery.prototype.updateQuery = function (mapFn) {
490 var queryManager = this.queryManager;
491 var result = queryManager.cache.diff({
492 query: this.options.query,
493 variables: this.variables,
494 returnPartialData: true,
495 optimistic: false,
496 }).result;
497 var newResult = mapFn(result, {
498 variables: this.variables,
499 });
500 if (newResult) {
501 queryManager.cache.writeQuery({
502 query: this.options.query,
503 data: newResult,
504 variables: this.variables,
505 });
506 queryManager.broadcastQueries();
507 }
508 };
509 /**
510 * A function that instructs the query to begin re-executing at a specified interval (in milliseconds).
511 */
512 ObservableQuery.prototype.startPolling = function (pollInterval) {
513 this.options.pollInterval = pollInterval;
514 this.updatePolling();
515 };
516 /**
517 * A function that instructs the query to stop polling after a previous call to `startPolling`.
518 */
519 ObservableQuery.prototype.stopPolling = function () {
520 this.options.pollInterval = 0;
521 this.updatePolling();
522 };
523 // Update options.fetchPolicy according to options.nextFetchPolicy.
524 ObservableQuery.prototype.applyNextFetchPolicy = function (reason,
525 // It's possible to use this method to apply options.nextFetchPolicy to
526 // options.fetchPolicy even if options !== this.options, though that happens
527 // most often when the options are temporary, used for only one request and
528 // then thrown away, so nextFetchPolicy may not end up mattering.
529 options) {
530 if (options.nextFetchPolicy) {
531 var _a = options.fetchPolicy, fetchPolicy = _a === void 0 ? "cache-first" : _a, _b = options.initialFetchPolicy, initialFetchPolicy = _b === void 0 ? fetchPolicy : _b;
532 if (fetchPolicy === "standby") {
533 // Do nothing, leaving options.fetchPolicy unchanged.
534 }
535 else if (typeof options.nextFetchPolicy === "function") {
536 // When someone chooses "cache-and-network" or "network-only" as their
537 // initial FetchPolicy, they often do not want future cache updates to
538 // trigger unconditional network requests, which is what repeatedly
539 // applying the "cache-and-network" or "network-only" policies would
540 // seem to imply. Instead, when the cache reports an update after the
541 // initial network request, it may be desirable for subsequent network
542 // requests to be triggered only if the cache result is incomplete. To
543 // that end, the options.nextFetchPolicy option provides an easy way to
544 // update options.fetchPolicy after the initial network request, without
545 // having to call observableQuery.setOptions.
546 options.fetchPolicy = options.nextFetchPolicy(fetchPolicy, {
547 reason: reason,
548 options: options,
549 observable: this,
550 initialFetchPolicy: initialFetchPolicy,
551 });
552 }
553 else if (reason === "variables-changed") {
554 options.fetchPolicy = initialFetchPolicy;
555 }
556 else {
557 options.fetchPolicy = options.nextFetchPolicy;
558 }
559 }
560 return options.fetchPolicy;
561 };
562 ObservableQuery.prototype.fetch = function (options, newNetworkStatus, query) {
563 // TODO Make sure we update the networkStatus (and infer fetchVariables)
564 // before actually committing to the fetch.
565 this.queryManager.setObservableQuery(this);
566 return this.queryManager["fetchConcastWithInfo"](this.queryId, options, newNetworkStatus, query);
567 };
568 // Turns polling on or off based on this.options.pollInterval.
569 ObservableQuery.prototype.updatePolling = function () {
570 var _this = this;
571 // Avoid polling in SSR mode
572 if (this.queryManager.ssrMode) {
573 return;
574 }
575 var _a = this, pollingInfo = _a.pollingInfo, pollInterval = _a.options.pollInterval;
576 if (!pollInterval || !this.hasObservers()) {
577 if (pollingInfo) {
578 clearTimeout(pollingInfo.timeout);
579 delete this.pollingInfo;
580 }
581 return;
582 }
583 if (pollingInfo && pollingInfo.interval === pollInterval) {
584 return;
585 }
586 invariant(pollInterval, 23);
587 var info = pollingInfo || (this.pollingInfo = {});
588 info.interval = pollInterval;
589 var maybeFetch = function () {
590 var _a, _b;
591 if (_this.pollingInfo) {
592 if (!isNetworkRequestInFlight(_this.queryInfo.networkStatus) &&
593 !((_b = (_a = _this.options).skipPollAttempt) === null || _b === void 0 ? void 0 : _b.call(_a))) {
594 _this.reobserve({
595 // Most fetchPolicy options don't make sense to use in a polling context, as
596 // users wouldn't want to be polling the cache directly. However, network-only and
597 // no-cache are both useful for when the user wants to control whether or not the
598 // polled results are written to the cache.
599 fetchPolicy: _this.options.initialFetchPolicy === "no-cache" ?
600 "no-cache"
601 : "network-only",
602 }, NetworkStatus.poll).then(poll, poll);
603 }
604 else {
605 poll();
606 }
607 }
608 };
609 var poll = function () {
610 var info = _this.pollingInfo;
611 if (info) {
612 clearTimeout(info.timeout);
613 info.timeout = setTimeout(maybeFetch, info.interval);
614 }
615 };
616 poll();
617 };
618 ObservableQuery.prototype.updateLastResult = function (newResult, variables) {
619 if (variables === void 0) { variables = this.variables; }
620 var error = this.getLastError();
621 // Preserve this.last.error unless the variables have changed.
622 if (error && this.last && !equal(variables, this.last.variables)) {
623 error = void 0;
624 }
625 return (this.last = __assign({ result: this.queryManager.assumeImmutableResults ?
626 newResult
627 : cloneDeep(newResult), variables: variables }, (error ? { error: error } : null)));
628 };
629 ObservableQuery.prototype.reobserveAsConcast = function (newOptions, newNetworkStatus) {
630 var _this = this;
631 this.isTornDown = false;
632 var useDisposableConcast =
633 // Refetching uses a disposable Concast to allow refetches using different
634 // options/variables, without permanently altering the options of the
635 // original ObservableQuery.
636 newNetworkStatus === NetworkStatus.refetch ||
637 // The fetchMore method does not actually call the reobserve method, but,
638 // if it did, it would definitely use a disposable Concast.
639 newNetworkStatus === NetworkStatus.fetchMore ||
640 // Polling uses a disposable Concast so the polling options (which force
641 // fetchPolicy to be "network-only" or "no-cache") won't override the original options.
642 newNetworkStatus === NetworkStatus.poll;
643 // Save the old variables, since Object.assign may modify them below.
644 var oldVariables = this.options.variables;
645 var oldFetchPolicy = this.options.fetchPolicy;
646 var mergedOptions = compact(this.options, newOptions || {});
647 var options = useDisposableConcast ?
648 // Disposable Concast fetches receive a shallow copy of this.options
649 // (merged with newOptions), leaving this.options unmodified.
650 mergedOptions
651 : assign(this.options, mergedOptions);
652 // Don't update options.query with the transformed query to avoid
653 // overwriting this.options.query when we aren't using a disposable concast.
654 // We want to ensure we can re-run the custom document transforms the next
655 // time a request is made against the original query.
656 var query = this.transformDocument(options.query);
657 this.lastQuery = query;
658 if (!useDisposableConcast) {
659 // We can skip calling updatePolling if we're not changing this.options.
660 this.updatePolling();
661 // Reset options.fetchPolicy to its original value when variables change,
662 // unless a new fetchPolicy was provided by newOptions.
663 if (newOptions &&
664 newOptions.variables &&
665 !equal(newOptions.variables, oldVariables) &&
666 // Don't mess with the fetchPolicy if it's currently "standby".
667 options.fetchPolicy !== "standby" &&
668 // If we're changing the fetchPolicy anyway, don't try to change it here
669 // using applyNextFetchPolicy. The explicit options.fetchPolicy wins.
670 (options.fetchPolicy === oldFetchPolicy ||
671 // A `nextFetchPolicy` function has even higher priority, though,
672 // so in that case `applyNextFetchPolicy` must be called.
673 typeof options.nextFetchPolicy === "function")) {
674 this.applyNextFetchPolicy("variables-changed", options);
675 if (newNetworkStatus === void 0) {
676 newNetworkStatus = NetworkStatus.setVariables;
677 }
678 }
679 }
680 this.waitForOwnResult && (this.waitForOwnResult = skipCacheDataFor(options.fetchPolicy));
681 var finishWaitingForOwnResult = function () {
682 if (_this.concast === concast) {
683 _this.waitForOwnResult = false;
684 }
685 };
686 var variables = options.variables && __assign({}, options.variables);
687 var _a = this.fetch(options, newNetworkStatus, query), concast = _a.concast, fromLink = _a.fromLink;
688 var observer = {
689 next: function (result) {
690 if (equal(_this.variables, variables)) {
691 finishWaitingForOwnResult();
692 _this.reportResult(result, variables);
693 }
694 },
695 error: function (error) {
696 if (equal(_this.variables, variables)) {
697 // Coming from `getResultsFromLink`, `error` here should always be an `ApolloError`.
698 // However, calling `concast.cancel` can inject another type of error, so we have to
699 // wrap it again here.
700 if (!isApolloError(error)) {
701 error = new ApolloError({ networkError: error });
702 }
703 finishWaitingForOwnResult();
704 _this.reportError(error, variables);
705 }
706 },
707 };
708 if (!useDisposableConcast && (fromLink || !this.concast)) {
709 // We use the {add,remove}Observer methods directly to avoid wrapping
710 // observer with an unnecessary SubscriptionObserver object.
711 if (this.concast && this.observer) {
712 this.concast.removeObserver(this.observer);
713 }
714 this.concast = concast;
715 this.observer = observer;
716 }
717 concast.addObserver(observer);
718 return concast;
719 };
720 ObservableQuery.prototype.reobserve = function (newOptions, newNetworkStatus) {
721 return this.reobserveAsConcast(newOptions, newNetworkStatus)
722 .promise;
723 };
724 ObservableQuery.prototype.resubscribeAfterError = function () {
725 var args = [];
726 for (var _i = 0; _i < arguments.length; _i++) {
727 args[_i] = arguments[_i];
728 }
729 // If `lastError` is set in the current when the subscription is re-created,
730 // the subscription will immediately receive the error, which will
731 // cause it to terminate again. To avoid this, we first clear
732 // the last error/result from the `observableQuery` before re-starting
733 // the subscription, and restore the last value afterwards so that the
734 // subscription has a chance to stay open.
735 var last = this.last;
736 this.resetLastResults();
737 var subscription = this.subscribe.apply(this, args);
738 this.last = last;
739 return subscription;
740 };
741 // (Re)deliver the current result to this.observers without applying fetch
742 // policies or making network requests.
743 ObservableQuery.prototype.observe = function () {
744 this.reportResult(
745 // Passing false is important so that this.getCurrentResult doesn't
746 // save the fetchMore result as this.lastResult, causing it to be
747 // ignored due to the this.isDifferentFromLastResult check in
748 // this.reportResult.
749 this.getCurrentResult(false), this.variables);
750 };
751 ObservableQuery.prototype.reportResult = function (result, variables) {
752 var lastError = this.getLastError();
753 var isDifferent = this.isDifferentFromLastResult(result, variables);
754 // Update the last result even when isDifferentFromLastResult returns false,
755 // because the query may be using the @nonreactive directive, and we want to
756 // save the the latest version of any nonreactive subtrees (in case
757 // getCurrentResult is called), even though we skip broadcasting changes.
758 if (lastError || !result.partial || this.options.returnPartialData) {
759 this.updateLastResult(result, variables);
760 }
761 if (lastError || isDifferent) {
762 iterateObserversSafely(this.observers, "next", result);
763 }
764 };
765 ObservableQuery.prototype.reportError = function (error, variables) {
766 // Since we don't get the current result on errors, only the error, we
767 // must mirror the updates that occur in QueryStore.markQueryError here
768 var errorResult = __assign(__assign({}, this.getLastResult()), { error: error, errors: error.graphQLErrors, networkStatus: NetworkStatus.error, loading: false });
769 this.updateLastResult(errorResult, variables);
770 iterateObserversSafely(this.observers, "error", (this.last.error = error));
771 };
772 ObservableQuery.prototype.hasObservers = function () {
773 return this.observers.size > 0;
774 };
775 ObservableQuery.prototype.tearDownQuery = function () {
776 if (this.isTornDown)
777 return;
778 if (this.concast && this.observer) {
779 this.concast.removeObserver(this.observer);
780 delete this.concast;
781 delete this.observer;
782 }
783 this.stopPolling();
784 // stop all active GraphQL subscriptions
785 this.subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
786 this.subscriptions.clear();
787 this.queryManager.stopQuery(this.queryId);
788 this.observers.clear();
789 this.isTornDown = true;
790 };
791 ObservableQuery.prototype.transformDocument = function (document) {
792 return this.queryManager.transform(document);
793 };
794 return ObservableQuery;
795}(Observable));
796export { ObservableQuery };
797// Necessary because the ObservableQuery constructor has a different
798// signature than the Observable constructor.
799fixObservableSubclass(ObservableQuery);
800// Reobserve with fetchPolicy effectively set to "cache-first", triggering
801// delivery of any new data from the cache, possibly falling back to the network
802// if any cache data are missing. This allows _complete_ cache results to be
803// delivered without also kicking off unnecessary network requests when
804// this.options.fetchPolicy is "cache-and-network" or "network-only". When
805// this.options.fetchPolicy is any other policy ("cache-first", "cache-only",
806// "standby", or "no-cache"), we call this.reobserve() as usual.
807export function reobserveCacheFirst(obsQuery) {
808 var _a = obsQuery.options, fetchPolicy = _a.fetchPolicy, nextFetchPolicy = _a.nextFetchPolicy;
809 if (fetchPolicy === "cache-and-network" || fetchPolicy === "network-only") {
810 return obsQuery.reobserve({
811 fetchPolicy: "cache-first",
812 // Use a temporary nextFetchPolicy function that replaces itself with the
813 // previous nextFetchPolicy value and returns the original fetchPolicy.
814 nextFetchPolicy: function (currentFetchPolicy, context) {
815 // Replace this nextFetchPolicy function in the options object with the
816 // original this.options.nextFetchPolicy value.
817 this.nextFetchPolicy = nextFetchPolicy;
818 // If the original nextFetchPolicy value was a function, give it a
819 // chance to decide what happens here.
820 if (typeof this.nextFetchPolicy === "function") {
821 return this.nextFetchPolicy(currentFetchPolicy, context);
822 }
823 // Otherwise go back to the original this.options.fetchPolicy.
824 return fetchPolicy;
825 },
826 });
827 }
828 return obsQuery.reobserve();
829}
830function defaultSubscriptionObserverErrorCallback(error) {
831 globalThis.__DEV__ !== false && invariant.error(24, error.message, error.stack);
832}
833export function logMissingFieldErrors(missing) {
834 if (globalThis.__DEV__ !== false && missing) {
835 globalThis.__DEV__ !== false && invariant.debug(25, missing);
836 }
837}
838function skipCacheDataFor(fetchPolicy /* `undefined` would mean `"cache-first"` */) {
839 return (fetchPolicy === "network-only" ||
840 fetchPolicy === "no-cache" ||
841 fetchPolicy === "standby");
842}
843//# sourceMappingURL=ObservableQuery.js.map
\No newline at end of file